home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / Managed / Common / dxmut.cs < prev    next >
Encoding:
Text File  |  2004-09-27  |  175.6 KB  |  3,962 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: DXMUT.cs
  3. //
  4. // DirectX SDK Managed Direct3D sample framework
  5. //
  6. // Copyright (c) Microsoft Corporation. All rights reserved.
  7. //--------------------------------------------------------------------------------------
  8. using System;
  9. using System.Collections;
  10. using Microsoft.DirectX;
  11. using Microsoft.DirectX.Direct3D;
  12.  
  13. namespace Microsoft.Samples.DirectX.UtilityToolkit
  14. {
  15.     /// <summary>
  16.     /// Managed Utility Framework Class
  17.     /// </summary>
  18.     public sealed class Framework : IDisposable
  19.     {
  20.  
  21.         #region Class Data
  22.         // Constants for command line arguments
  23.         private const string CmdAdapter = "adapter";
  24.         private const string CmdWindowed = "windowed";
  25.         private const string CmdFullscreen = "fullscreen";
  26.         private const string CmdForceHardware = "forcehal";
  27.         private const string CmdForceRef = "forceref";
  28.         private const string CmdPureHwVp = "forcepurehwvp";
  29.         private const string CmdForceHwVp = "forcehwvp";
  30.         private const string CmdForceSwVp = "forceswvp";
  31.         private const string CmdWidth = "width";
  32.         private const string CmdHeight = "height";
  33.         private const string CmdStartx = "startx";
  34.         private const string CmdStarty = "starty";
  35.         private const string CmdConstantFrame = "constantframetime";
  36.         private const string CmdQuitAfterFrame = "quitafterframe";
  37.         private const string CmdNoErrorBoxes = "noerrormsgboxes";
  38.  
  39.         // Constants for minimum window size
  40.         private const int MinimumWindowSizeX = 200;
  41.         private const int MinimumWindowSizeY = 200;
  42.         public const int DefaultSizeWidth = 640;
  43.         public const int DefaultSizeHeight = 480;
  44.         // Default starting size
  45.         private static readonly System.Drawing.Size DefaultStartingSize = new System.Drawing.Size(DefaultSizeWidth, DefaultSizeHeight);
  46.         private static readonly System.Drawing.Point MinWindowSize = new System.Drawing.Point(MinimumWindowSizeX, MinimumWindowSizeY);
  47.         public static readonly IntPtr TrueIntPtr = new IntPtr(1);
  48.         private const string WindowClassName = "ManagedDirect3DWindowClass";
  49.  
  50.         // What about the stored data?
  51.         private FrameworkData State;
  52.         // Last shown error
  53.         private Exception lastDisplayedMessage = null;
  54.         // Has the framework been disposed already
  55.         private bool isDisposed = false;
  56.         #endregion
  57.  
  58.         #region Creation 
  59.         /// <summary>
  60.         /// Constructor, init data here
  61.         /// </summary>
  62.         public Framework()
  63.         {
  64.             State = new FrameworkData();
  65.             // Store the window proc delegate here, it may get used a few times
  66.             State.WindowProcDelegate = new NativeMethods.WndProcDelegate(WindowsProcedure);
  67.         }
  68.         #endregion
  69.  
  70.         #region Setting up callbacks
  71.         // Interface 'callbacks'
  72.         public void SetCallbackInterface(IFrameworkCallback callback) { State.CallbackInterface = callback; }
  73.         public void SetDeviceCreationInterface(IDeviceCreation callback) { State.DeviceCreationInterface = callback; }
  74.  
  75.         // Event 'callbacks'
  76.  
  77.         /// <summary>The event fired for disposing of the device</summary>
  78.         public event EventHandler Disposing; 
  79.         /// <summary>The event fired for when the device is lost</summary>
  80.         public event EventHandler DeviceLost; 
  81.         /// <summary>The event fired for when the device is created</summary>
  82.         public event DeviceEventHandler DeviceCreated; 
  83.         /// <summary>The event fired for when the device is reset</summary>
  84.         public event DeviceEventHandler DeviceReset; 
  85.  
  86.         public void SetKeyboardCallback(KeyboardCallback callback) { State.KeyboardFunction = callback; }
  87.         public void SetMouseCallback(MouseCallback callback) { State.MouseFunction = callback; }
  88.         public void SetWndProcCallback(WndProcCallback callback) { State.WndProcFunction = callback; }
  89.         #endregion
  90.  
  91.         /// <summary>
  92.         /// Optionally parses the command line and sets if default hotkeys are handled
  93.         /// Possible command line parameters are:
  94.         /// -adapter:#              forces app to use this adapter # (fails if the adapter doesn't exist)
  95.         /// -windowed               forces app to start windowed
  96.         /// -fullscreen             forces app to start full screen
  97.         /// -forceHardware          forces app to use Hardware (fails if Hardware doesn't exist)
  98.         /// -forceReference         forces app to use Reference (fails if Reference doesn't exist)
  99.         /// -forcepurehwvp          forces app to use pure HWVP (fails if device doesn't support it)
  100.         /// -forcehwvp              forces app to use HWVP (fails if device doesn't support it)
  101.         /// -forceswvp              forces app to use SWVP 
  102.         /// -width:#                forces app to use # for width. for full screen, it will pick the closest possible supported mode
  103.         /// -height:#               forces app to use # for height. for full screen, it will pick the closest possible supported mode
  104.         /// -startx:#               forces app to use # for the x coord of the window position for windowed mode
  105.         /// -starty:#               forces app to use # for the y coord of the window position for windowed mode
  106.         /// -constantframetime:#    forces app to use constant frame time, where # is the time/frame in seconds
  107.         /// -quitafterframe:x       forces app to quit after # frames
  108.         /// -noerrormsgboxes        prevents the display of message boxes generated by the framework so the application can be run without user interaction
  109.         /// 
  110.         /// Hotkeys handled by default are:
  111.         /// ESC                 app exits
  112.         /// Alt-Enter           toggle between full screen & windowed
  113.         /// F2                     device selection dialog
  114.         /// F3                     toggle Hardware/Reference
  115.         /// F8                  toggle wire-frame mode
  116.         /// Pause               pauses time
  117.         /// </summary>
  118.         public void Initialize(bool isParsingCommandLine, bool handleDefaultKeys, bool showMessageBoxOnError)
  119.         {
  120.             State.WasInitCalled = true;
  121.  
  122.             // Increase
  123.  
  124.             // Store this data
  125.             State.IsShowingMsgBoxOnError = showMessageBoxOnError;
  126.             State.IsHandlingDefaultHotkeys = handleDefaultKeys;
  127.  
  128.             // Reset the timer period (ignore any failures)
  129.             try
  130.             { NativeMethods.timeBeginPeriod(1); }
  131.             catch { System.Diagnostics.Debugger.Log(9, string.Empty, "Could not set time period.\r\n" ); }
  132.  
  133.             if (isParsingCommandLine)
  134.             {
  135.                 ParseCommandLine();
  136.             }
  137.  
  138.             // Reset the timer
  139.             FrameworkTimer.Reset();
  140.             State.IsInited = true;
  141.  
  142.         }
  143.  
  144.         /// <summary>
  145.         /// Parses the command line for parameters.  See Initialize() for list 
  146.         /// </summary>
  147.         private void ParseCommandLine()
  148.         {
  149.             string[] args = System.Environment.GetCommandLineArgs(); // Ignore the first one since that's the executable name
  150.  
  151.             // Go through each argument, skipping the first one
  152.             for (int i = 1; i < args.Length; i++)
  153.             {
  154.                 string argument = string.Empty;
  155.                 if ((args[i].StartsWith("-")) || (args[i].StartsWith("-")))
  156.                 {
  157.                     argument = args[i].Substring(1, args[i].Length - 1);
  158.                 }
  159.                 else
  160.                     continue;
  161.  
  162.                 if (argument.ToLower().StartsWith(CmdAdapter + ":"))
  163.                 {
  164.                     try
  165.                     { State.OverrideAdapterOrdinal = int.Parse(argument.Substring(CmdAdapter.Length + 1, argument.Length - (CmdAdapter.Length + 1))); }
  166.                     catch (FormatException){}
  167.                 }
  168.                 else if (string.Compare(argument, CmdWindowed, true) == 0)
  169.                 {
  170.                     State.IsOverridingWindowed = true;
  171.                 }
  172.                 else if (string.Compare(argument, CmdFullscreen, true) == 0)
  173.                 {
  174.                     State.IsOverridingFullScreen = true;
  175.                 }
  176.                 else if (string.Compare(argument, CmdForceHardware, true) == 0)
  177.                 {
  178.                     State.IsOverridingForceHardware = true;
  179.                 }
  180.                 else if (string.Compare(argument, CmdForceRef, true) == 0)
  181.                 {
  182.                     State.IsOverridingForceReference = true;
  183.                 }
  184.                 else if (string.Compare(argument, CmdPureHwVp, true) == 0)
  185.                 {
  186.                     State.IsOverridingForcePureHardwareVertexProcessing = true;
  187.                 }
  188.                 else if (string.Compare(argument, CmdForceHwVp, true) == 0)
  189.                 {
  190.                     State.IsOverridingForceHardwareVertexProcessing = true;
  191.                 }
  192.                 else if (string.Compare(argument, CmdForceSwVp, true) == 0)
  193.                 {
  194.                     State.IsOverridingForceSoftwareVertexProcessing = true;
  195.                 }
  196.                 else if (argument.ToLower().StartsWith(CmdWidth + ":"))
  197.                 {
  198.                     try
  199.                     { State.OverrideWidth = int.Parse(argument.Substring(CmdWidth.Length + 1, argument.Length - (CmdWidth.Length + 1))); }
  200.                     catch (FormatException){}
  201.                 }
  202.                 else if (argument.ToLower().StartsWith(CmdHeight + ":"))
  203.                 {
  204.                     try
  205.                     { State.OverrideHeight = int.Parse(argument.Substring(CmdHeight.Length + 1, argument.Length - (CmdHeight.Length + 1))); }
  206.                     catch (FormatException){}
  207.                 }
  208.                 else if (argument.ToLower().StartsWith(CmdStartx + ":"))
  209.                 {
  210.                     try
  211.                     { State.OverrideStartX = int.Parse(argument.Substring(CmdStartx.Length + 1, argument.Length - (CmdStartx.Length + 1))); }
  212.                     catch (FormatException){}
  213.                 }
  214.                 else if (argument.ToLower().StartsWith(CmdStarty + ":"))
  215.                 {
  216.                     try
  217.                     { State.OverrideStartY = int.Parse(argument.Substring(CmdStarty.Length + 1, argument.Length - (CmdStarty.Length + 1))); }
  218.                     catch (FormatException){}
  219.                 }
  220.                 else if (argument.ToLower().StartsWith(CmdConstantFrame + ":"))
  221.                 {
  222.                     float timePerFrame = 0.033f; // Default to this time
  223.                     try
  224.                     { 
  225.                         timePerFrame = float.Parse(argument.Substring(CmdConstantFrame.Length + 1, argument.Length - (CmdConstantFrame.Length + 1))); 
  226.                     }
  227.                     catch (FormatException){}
  228.                     State.OverrideConstantTimePerFrame = timePerFrame;
  229.                     State.IsOverridingConstantFrameTime = true;
  230.                 }
  231.                 else if (argument.ToLower().StartsWith(CmdQuitAfterFrame + ":"))
  232.                 {
  233.                     try
  234.                     { State.OverrideQuitAfterFrame = int.Parse(argument.Substring(CmdQuitAfterFrame.Length + 1, argument.Length - (CmdQuitAfterFrame.Length + 1))); }
  235.                     catch (FormatException){}
  236.                 }
  237.                 else if (string.Compare(argument, CmdNoErrorBoxes, true) == 0)
  238.                 {
  239.                     State.IsShowingMsgBoxOnError = false;
  240.                 }
  241.                 else
  242.                     System.Diagnostics.Debugger.Log(9, string.Empty, string.Format("Unrecognized flag: {0}\r\n", argument));
  243.             }
  244.         }
  245.  
  246.         /// <summary>
  247.         /// Creates a window with the specified title, icon, menu, and starting position.  If Init hasn't been
  248.         /// called, this will call with default params.  Instead of calling this, you can call SetWindow
  249.         /// to use an existing window
  250.         /// </summary>
  251.         public void CreateWindow(string windowTitle, System.Drawing.Icon icon, 
  252.             System.Windows.Forms.MainMenu menu, int x, int y)
  253.         {
  254.             if (State.IsInsideDeviceCallback)
  255.                 throw new InvalidOperationException("You cannot create a window from inside a callback.");
  256.  
  257.             State.WasWindowCreateCalled = true;
  258.             // Are we inited?
  259.             if (!State.IsInited)
  260.             {
  261.                 // If Init was already called and failed, fail again
  262.                 if (State.WasInitCalled)
  263.                 {
  264.                     throw new InvalidOperationException("Initialize was already called and failed.");
  265.                 }
  266.  
  267.                 // Call initialize with default params
  268.                 Initialize(true, true, true);
  269.             }
  270.  
  271.             // Is there already a window created?
  272.             if (State.WindowFocus == IntPtr.Zero)
  273.             {
  274.                 // Register the window class
  275.                 NativeMethods.WindowClass wndClass = new NativeMethods.WindowClass();
  276.                 wndClass.Styles = 0x0008; // Double click class style
  277.                 wndClass.WindowsProc = State.WindowProcDelegate;
  278.                 wndClass.IconHandle = (icon != null) ? icon.Handle : IntPtr.Zero;
  279.                 wndClass.CursorHandle = System.Windows.Forms.Cursors.Default.Handle;
  280.                 wndClass.ClassName = WindowClassName;
  281.  
  282.                 // Register this class
  283.                 NativeMethods.RegisterClass(ref wndClass);
  284.                 
  285.                 // Override the window's initial size and position if there were cmd line args
  286.                 if (State.OverrideStartX != -1)
  287.                 {
  288.                     State.DefaultStartingLocation = System.Windows.Forms.FormStartPosition.Manual;
  289.                     x = State.OverrideStartX;
  290.                 }
  291.                 if (State.OverrideStartY != -1)
  292.                 {
  293.                     State.DefaultStartingLocation = System.Windows.Forms.FormStartPosition.Manual;
  294.                     y = State.OverrideStartY;
  295.                 }
  296.  
  297.                 // Are we in the default start location?
  298.                 if (State.DefaultStartingLocation == System.Windows.Forms.FormStartPosition.WindowsDefaultLocation)
  299.                 {
  300.                     State.IsWindowCreatedWithDefaultPositions = true;
  301.                     x = unchecked((int)0x80000000);
  302.                     y = unchecked((int)0x80000000);
  303.                 }
  304.                 else
  305.                 {
  306.                     State.IsWindowCreatedWithDefaultPositions = false;
  307.                 }
  308.  
  309.                 // Calculate the window size
  310.                 System.Drawing.Size windowSize = DefaultStartingSize;
  311.                 if (State.OverrideWidth != 0)
  312.                     windowSize.Width = State.OverrideWidth;
  313.                 if (State.OverrideHeight != 0)
  314.                     windowSize.Height = State.OverrideHeight;
  315.                 
  316.                 // Set the window's initial style.  It is invisible initially since it might
  317.                 // be resized later
  318.                 NativeMethods.WindowStyles style = NativeMethods.WindowStyles.Overlapped | NativeMethods.WindowStyles.Caption |
  319.                     NativeMethods.WindowStyles.SystemMenu | NativeMethods.WindowStyles.ThickFrame | NativeMethods.WindowStyles.MinimizeBox |
  320.                     NativeMethods.WindowStyles.MaximizeBox;
  321.  
  322.                 System.Drawing.Rectangle r = new System.Drawing.Rectangle(System.Drawing.Point.Empty, windowSize);
  323.                 NativeMethods.AdjustWindowRect(ref r, style, (menu != null));
  324.  
  325.                 // Size the window
  326.                 windowSize.Width = r.Width - r.Left;
  327.                 windowSize.Height = r.Height - r.Top;
  328.  
  329.                 State.WindowStyle = style;
  330.  
  331.                 // Store the window title
  332.                 State.WindowTitle = windowTitle;
  333.  
  334.                 // Set current cursor
  335.                 System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
  336.  
  337.                 // Create the window now
  338.                 IntPtr renderWindow = NativeMethods.CreateWindow(0, WindowClassName, windowTitle,
  339.                     style, x, y, windowSize.Width, windowSize.Height, IntPtr.Zero, (menu != null) ? menu.Handle : IntPtr.Zero,
  340.                     IntPtr.Zero, IntPtr.Zero);
  341.  
  342.                 // Get the window's rectangles
  343.                 System.Drawing.Rectangle rect;
  344.                 NativeMethods.GetClientRect(renderWindow, out rect);
  345.                 State.ClientRectangle = rect;
  346.                 NativeMethods.GetWindowRect(renderWindow, out rect);
  347.                 State.WindowBoundsRectangle = rect;
  348.  
  349.                 // Update state
  350.                 State.WasWindowCreated = true;
  351.                 State.WindowFocus = renderWindow;
  352.                 State.WindowDeviceFullScreen = renderWindow;
  353.                 State.WindowDeviceWindowed = renderWindow;
  354.             }
  355.         }
  356.  
  357.         /// <summary>
  358.         /// Calls into CreateWindow with default paramters
  359.         /// </summary>
  360.         public void CreateWindow(string windowTitle)
  361.         {
  362.             CreateWindow(windowTitle, LoadFirstIconFromResource(), null, -1, -1);
  363.         }
  364.         /// <summary>
  365.         /// Sets a previously created window for the framework to use.  If Init has
  366.         /// not already been called, it will call it with default parameters.  Instead
  367.         /// of calling this you can call CreateWindow to create a new window.
  368.         /// </summary>
  369.         public void SetWindow(IntPtr windowFocus, IntPtr windowDeviceFullscreen,
  370.             IntPtr windowDeviceWindowed, bool handleMessages)
  371.         {
  372.             if (State.IsInsideDeviceCallback)
  373.                 throw new InvalidOperationException("You cannot create a window from inside a callback.");
  374.  
  375.             State.WasWindowCreateCalled = true;
  376.  
  377.             // To avoid confusion, we do not allow any window handles to be null here.  The
  378.             // caller must pass in valid window handles for all three parameters.  The same
  379.             // window handles may be used for more than one parameter.
  380.             if ((windowDeviceFullscreen == IntPtr.Zero) || (windowDeviceWindowed == IntPtr.Zero)
  381.                 || (windowFocus == IntPtr.Zero))
  382.             {
  383.                 throw new InvalidOperationException("You must pass in valid window handles.");
  384.             }
  385.  
  386.             if (handleMessages)
  387.             {
  388.                 // Use the static windows procedure defined here
  389.                 NativeMethods.HookWindowsMessages(windowFocus, State.WindowProcDelegate);
  390.             }
  391.  
  392.             // Are we inited?
  393.             if (!State.IsInited)
  394.             {
  395.                 // If Init was already called and failed, fail again
  396.                 if (State.WasInitCalled)
  397.                 {
  398.                     throw new InvalidOperationException("Initialize was already called and failed.");
  399.                 }
  400.  
  401.                 // Call initialize with default params
  402.                 Initialize(true, true, true);
  403.             }
  404.  
  405.             // Get the window's rectangles
  406.             System.Drawing.Rectangle rect;
  407.             NativeMethods.GetClientRect(windowDeviceWindowed, out rect);
  408.             State.ClientRectangle = rect;
  409.             NativeMethods.GetWindowRect(windowDeviceWindowed, out rect);
  410.             State.WindowBoundsRectangle = rect;
  411.  
  412.             // Update state
  413.             State.WasWindowCreated = true;
  414.             State.WindowFocus = windowFocus;
  415.             State.WindowDeviceFullScreen = windowDeviceFullscreen;
  416.             State.WindowDeviceWindowed = windowDeviceWindowed;
  417.         }
  418.  
  419.         /// <summary>
  420.         /// Creates a Direct3D device.  If CreateWindow or SetWindow has not already
  421.         /// been called, it will call CreateWindow with default parameters.
  422.         /// Instead of calling this, you can call SetDevice or CreateDeviceFromSettings
  423.         /// </summary>
  424.         public void CreateDevice(uint adapterOridinal, bool windowed, int suggestedWidth,
  425.             int suggestedHeight, IDeviceCreation callback)
  426.         {
  427.             if (State.IsInsideDeviceCallback)
  428.                 throw new InvalidOperationException("You cannot create a window from inside a callback.");
  429.  
  430.             // Store callbacks in global state
  431.             SetDeviceCreationInterface(callback);
  432.  
  433.             // Update device create being called
  434.             State.WasDeviceCreateCalled = true;
  435.  
  436.             // Was the window created? If not, create it now
  437.             if (!State.WasWindowCreated)
  438.             {
  439.                 // If CreateWindow or SetWindow was already called and failed, then fail again.
  440.                 if (State.WasWindowCreateCalled)
  441.                 {
  442.                     throw new InvalidOperationException("CreateWindow was already called and failed.");
  443.                 }
  444.  
  445.                 // Create a default window
  446.                 CreateWindow("Direct3D Window", null, null, -1, -1);
  447.             }
  448.  
  449.             // Force an enumeration with the updated Device Acceptable callback
  450.             Enumeration.Enumerate(State.DeviceCreationInterface);
  451.  
  452.             // Set up the match options
  453.             MatchOptions match = new MatchOptions();
  454.             match.AdapterOrdinal = MatchType.PreserveInput;
  455.             match.DeviceType = MatchType.IgnoreInput;
  456.             match.Windowed = MatchType.PreserveInput;
  457.             match.AdapterFormat = MatchType.IgnoreInput;
  458.             match.VertexProcessing = MatchType.IgnoreInput;
  459.             match.Resolution = MatchType.ClosestToInput;
  460.             match.BackBufferFormat = MatchType.IgnoreInput;
  461.             match.BackBufferCount = MatchType.IgnoreInput;
  462.             match.MultiSample = MatchType.IgnoreInput;
  463.             match.SwapEffect = MatchType.IgnoreInput;
  464.             match.DepthFormat = MatchType.IgnoreInput;
  465.             match.StencilFormat = MatchType.IgnoreInput;
  466.             match.PresentFlags = MatchType.IgnoreInput;
  467.             match.RefreshRate = MatchType.IgnoreInput;
  468.             match.PresentInterval = MatchType.IgnoreInput;
  469.  
  470.             // Get the device settings
  471.             DeviceSettings settings = new DeviceSettings();
  472.             settings.presentParams = new PresentParameters();
  473.             settings.AdapterOrdinal = adapterOridinal;
  474.             settings.presentParams.Windowed = windowed;
  475.             settings.presentParams.BackBufferWidth = suggestedWidth;
  476.             settings.presentParams.BackBufferHeight = suggestedHeight;
  477.  
  478.             // Override with settings for command line
  479.             if (State.OverrideWidth != 0)
  480.                 settings.presentParams.BackBufferWidth = State.OverrideWidth;
  481.             if (State.OverrideHeight != 0)
  482.                 settings.presentParams.BackBufferHeight = State.OverrideHeight;
  483.             if (State.OverrideAdapterOrdinal != -1)
  484.                 settings.AdapterOrdinal = (uint)State.OverrideAdapterOrdinal;
  485.             
  486.             if (State.IsOverridingFullScreen)
  487.             {
  488.                 settings.presentParams.Windowed = false;
  489.                 if ((State.OverrideWidth == 0) && (State.OverrideHeight == 0))
  490.                     match.Resolution = MatchType.IgnoreInput;
  491.             }
  492.             if (State.IsOverridingWindowed)
  493.                 settings.presentParams.Windowed = true;
  494.  
  495.             if (State.IsOverridingForceHardware)
  496.             {
  497.                 settings.DeviceType = DeviceType.Hardware;
  498.                 match.DeviceType = MatchType.PreserveInput;
  499.             }
  500.             if (State.IsOverridingForceReference)
  501.             {
  502.                 settings.DeviceType = DeviceType.Reference;
  503.                 match.DeviceType = MatchType.PreserveInput;
  504.             }
  505.             if (State.IsOverridingForcePureHardwareVertexProcessing)
  506.             {
  507.                 settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing | CreateFlags.PureDevice;
  508.                 match.VertexProcessing = MatchType.PreserveInput;
  509.             }
  510.             if (State.IsOverridingForceHardwareVertexProcessing)
  511.             {
  512.                 settings.BehaviorFlags = CreateFlags.HardwareVertexProcessing;
  513.                 match.VertexProcessing = MatchType.PreserveInput;
  514.             }
  515.             if (State.IsOverridingForceSoftwareVertexProcessing)
  516.             {
  517.                 settings.BehaviorFlags = CreateFlags.SoftwareVertexProcessing;
  518.                 match.VertexProcessing = MatchType.PreserveInput;
  519.             }
  520.  
  521.             try
  522.             {
  523.                 settings = FindValidDeviceSettings(settings, match);
  524.             }
  525.             catch(Exception e)
  526.             {
  527.                 DisplayErrorMessage(e);
  528.                 throw;
  529.             }
  530.  
  531.             // If the modify device callback isn't null, call it to
  532.             // let the app change the settings
  533.             if (State.DeviceCreationInterface != null)
  534.             {
  535.                 Caps c = Manager.GetDeviceCaps((int)settings.AdapterOrdinal, settings.DeviceType);
  536.                 State.DeviceCreationInterface.ModifyDeviceSettings(settings, c);
  537.             }
  538.  
  539.             // Change to a Direct3D device created from the new device settings
  540.             // If there is an existing device, either reset or recreate
  541.             ChangeDevice(settings, null, false);
  542.         }
  543.  
  544.  
  545.         /// <summary>
  546.         /// Passes a previously created Direct3D device for use by the framework.  
  547.         /// If CreateWindow() has not already been called, it will call it with the 
  548.         /// default parameters.  Instead of calling this, you can call CreateDevice() or 
  549.         /// CreateDeviceFromSettings() 
  550.         /// </summary>
  551.         public void SetDevice(Device device)
  552.         {
  553.             if (device == null)
  554.                 throw new ArgumentNullException("device", "You cannot pass in a null device to SetDevice");
  555.  
  556.             if (State.IsInsideDeviceCallback)
  557.                 throw new InvalidOperationException("You cannot set a device from inside a callback.");
  558.  
  559.             // Was the window created? If not, create it now
  560.             if (!State.WasWindowCreated)
  561.             {
  562.                 // If CreateWindow or SetWindow was already called and failed, then fail again.
  563.                 if (State.WasWindowCreateCalled)
  564.                 {
  565.                     throw new InvalidOperationException("CreateWindow was already called and failed.");
  566.                 }
  567.  
  568.                 // Create a default window
  569.                 CreateWindow("Direct3D Window", null, null, -1, -1);
  570.             }
  571.  
  572.             DeviceSettings deviceSettings = new DeviceSettings();
  573.  
  574.             // Get the present parameters from the swap chain
  575.             using(Surface backBuffer = device.GetBackBuffer(0, 0, BackBufferType.Mono))
  576.             {
  577.                 using (SwapChain swap = backBuffer.GetContainer(InterfaceGuid.SwapChain) as SwapChain)
  578.                 {
  579.                     deviceSettings.presentParams = swap.PresentParameters;
  580.                     System.Diagnostics.Debug.Assert(deviceSettings.presentParams != null, "You must have valid present parameters here.");
  581.                 }
  582.             }
  583.  
  584.             DeviceCreationParameters creationParams = device.CreationParameters;
  585.  
  586.             // Fill out the device settings structure now
  587.             deviceSettings.AdapterOrdinal = (uint)creationParams.AdapterOrdinal;    
  588.             deviceSettings.DeviceType = creationParams.DeviceType;
  589.             deviceSettings.AdapterFormat = FindAdapterFormat(deviceSettings.AdapterOrdinal,
  590.                 deviceSettings.DeviceType, deviceSettings.presentParams.BackBufferFormat,
  591.                 deviceSettings.presentParams.Windowed);
  592.             deviceSettings.BehaviorFlags = creationParams.Behavior.Value;
  593.  
  594.             // Change to the Direct3D device passed in
  595.             ChangeDevice(deviceSettings, device, false);
  596.         }
  597.  
  598.         /// <summary>
  599.         /// Tells the framework to change to a device created from the passed in device settings
  600.         /// If CreateWindow() has not already been called, it will call it with the 
  601.         /// default parameters.  Instead of calling this, you can call CreateDevice() 
  602.         /// or SetDevice() 
  603.         /// </summary>
  604.         public void CreateDeviceFromSettings(DeviceSettings deviceSettings, bool preserveInput)
  605.         {
  606.             // Set the state since this was called
  607.             State.WasDeviceCreateCalled = true;
  608.  
  609.             // Was the window created? If not, create it now
  610.             if (!State.WasWindowCreated)
  611.             {
  612.                 // If CreateWindow or SetWindow was already called and failed, then fail again.
  613.                 if (State.WasWindowCreateCalled)
  614.                 {
  615.                     throw new InvalidOperationException("CreateWindow was already called and failed.");
  616.                 }
  617.  
  618.                 // Create a default window
  619.                 CreateWindow("Direct3D Window", null, null, -1, -1);
  620.             }
  621.  
  622.             if (!preserveInput)
  623.             {
  624.                 // If not preserving the input, the find the closest valid to it
  625.                 MatchOptions match = new MatchOptions();
  626.                 match.AdapterOrdinal = MatchType.ClosestToInput;
  627.                 match.DeviceType = MatchType.ClosestToInput;
  628.                 match.Windowed = MatchType.ClosestToInput;
  629.                 match.AdapterFormat = MatchType.ClosestToInput;
  630.                 match.VertexProcessing = MatchType.ClosestToInput;
  631.                 match.Resolution = MatchType.ClosestToInput;
  632.                 match.BackBufferFormat = MatchType.ClosestToInput;
  633.                 match.BackBufferCount = MatchType.ClosestToInput;
  634.                 match.MultiSample = MatchType.ClosestToInput;
  635.                 match.SwapEffect = MatchType.ClosestToInput;
  636.                 match.DepthFormat = MatchType.ClosestToInput;
  637.                 match.StencilFormat = MatchType.ClosestToInput;
  638.                 match.PresentFlags = MatchType.ClosestToInput;
  639.                 match.RefreshRate = MatchType.ClosestToInput;
  640.                 match.PresentInterval = MatchType.ClosestToInput;
  641.  
  642.                 try
  643.                 {
  644.                     deviceSettings = FindValidDeviceSettings(deviceSettings, match);
  645.                 }
  646.                 catch(Exception e)
  647.                 {
  648.                     // Display any error message
  649.                     DisplayErrorMessage(e);
  650.                     throw;
  651.                 }
  652.                 // Change to a Direct3D device created from the new device settings.  
  653.                 // If there is an existing device, then either reset or recreate the scene
  654.                 ChangeDevice(deviceSettings, null, false);
  655.             }
  656.         }
  657.         /// <summary>
  658.         /// Tells the framework to change to a device created from the passed in device settings
  659.         /// If CreateWindow() has not already been called, it will call it with the 
  660.         /// default parameters.  Instead of calling this, you can call CreateDevice() 
  661.         /// or SetDevice() 
  662.         /// </summary>
  663.         public void CreateDeviceFromSettings(DeviceSettings deviceSettings) { CreateDeviceFromSettings(deviceSettings, false); }
  664.  
  665.         /// <summary>
  666.         /// Toggle between full screen and windowed
  667.         /// </summary>
  668.         public void ToggleFullscreen()
  669.         {
  670.             // Pause the application
  671.             Pause(true, true);
  672.  
  673.             // Get the current device settings and flip the windowed state then
  674.             // find the closest valid device settings with this change
  675.             DeviceSettings currentSettings = State.CurrentDeviceSettings.Clone();
  676.             currentSettings.presentParams.Windowed = !currentSettings.presentParams.Windowed;
  677.  
  678.             MatchOptions match = new MatchOptions();
  679.             match.AdapterOrdinal = MatchType.PreserveInput;
  680.             match.DeviceType = MatchType.ClosestToInput;
  681.             match.Windowed = MatchType.PreserveInput;
  682.             match.AdapterFormat = MatchType.IgnoreInput;
  683.             match.VertexProcessing = MatchType.ClosestToInput;
  684.             match.BackBufferFormat = MatchType.IgnoreInput;
  685.             match.BackBufferCount = MatchType.ClosestToInput;
  686.             match.MultiSample = MatchType.ClosestToInput;
  687.             match.SwapEffect = MatchType.ClosestToInput;
  688.             match.DepthFormat = MatchType.ClosestToInput;
  689.             match.StencilFormat = MatchType.ClosestToInput;
  690.             match.PresentFlags = MatchType.ClosestToInput;
  691.             match.RefreshRate = MatchType.IgnoreInput;
  692.             match.PresentInterval = MatchType.IgnoreInput;
  693.  
  694.             System.Drawing.Rectangle windowClient;
  695.             if (currentSettings.presentParams.Windowed)
  696.                 windowClient = State.ClientRectangle;
  697.             else
  698.                 windowClient = State.FullScreenClientRectangle;
  699.  
  700.             if (windowClient.Width > 0 && windowClient.Height > 0)
  701.             {
  702.                 match.Resolution = MatchType.ClosestToInput;
  703.                 currentSettings.presentParams.BackBufferWidth = windowClient.Width;
  704.                 currentSettings.presentParams.BackBufferHeight = windowClient.Height;
  705.             }
  706.             else
  707.             {
  708.                 match.Resolution = MatchType.IgnoreInput;
  709.             }
  710.  
  711.             try
  712.             {
  713.                 currentSettings = FindValidDeviceSettings(currentSettings, match);
  714.                 ChangeDevice(currentSettings, null, false);
  715.             }
  716.             finally
  717.             {
  718.                 // Well, unpause no matter what
  719.                 Pause(false, false);
  720.                 State.CurrentDeviceSettings = currentSettings;
  721.             }
  722.         }
  723.  
  724.         
  725.         /// <summary>
  726.         /// Toggle between Hardware and Reference
  727.         /// </summary>
  728.         public void ToggleReference()
  729.         {
  730.             // Get the current device settings 
  731.             DeviceSettings currentSettings = State.CurrentDeviceSettings.Clone();
  732.             if (currentSettings.DeviceType == DeviceType.Hardware)
  733.                 currentSettings.DeviceType = DeviceType.Reference;
  734.             else if (currentSettings.DeviceType == DeviceType.Reference)
  735.                 currentSettings.DeviceType = DeviceType.Hardware;
  736.  
  737.             MatchOptions match = new MatchOptions();
  738.             match.AdapterOrdinal = MatchType.PreserveInput;
  739.             match.DeviceType = MatchType.PreserveInput;
  740.             match.Windowed = MatchType.ClosestToInput;
  741.             match.AdapterFormat = MatchType.ClosestToInput;
  742.             match.VertexProcessing = MatchType.ClosestToInput;
  743.             match.Resolution = MatchType.ClosestToInput;
  744.             match.BackBufferFormat = MatchType.ClosestToInput;
  745.             match.BackBufferCount = MatchType.ClosestToInput;
  746.             match.MultiSample = MatchType.ClosestToInput;
  747.             match.SwapEffect = MatchType.ClosestToInput;
  748.             match.DepthFormat = MatchType.ClosestToInput;
  749.             match.StencilFormat = MatchType.ClosestToInput;
  750.             match.PresentFlags = MatchType.ClosestToInput;
  751.             match.RefreshRate = MatchType.ClosestToInput;
  752.             match.PresentInterval = MatchType.ClosestToInput;
  753.  
  754.             try
  755.             {
  756.                 currentSettings = FindValidDeviceSettings(currentSettings, match);
  757.                 ChangeDevice(currentSettings, null, false);
  758.             }
  759. #if(DEBUG)
  760.             catch (Exception e)
  761.             {
  762.                 // In debug mode show this error (maybe - depending on settings)
  763.                 DisplayErrorMessage(e);
  764. #else
  765.             catch
  766.             {
  767.                 // In release mode fail silently
  768. #endif
  769.             }
  770.             finally
  771.             {
  772.                 // Update the current settings
  773.                 State.CurrentDeviceSettings = currentSettings;
  774.             }
  775.         }
  776.  
  777.         /// <summary>
  778.         /// This function tries to find valid device settings based upon the input device settings 
  779.         /// struct and the match options.  For each device setting a match option in the 
  780.         /// MatchOptions struct specifies how the function makes decisions.  For example, if 
  781.         /// the caller wants a hardware device with a back buffer format of A2B10G10R10 but the 
  782.         /// hardware device on the system does not support A2B10G10R10 however a reference device is 
  783.         /// installed that does, then the function has a choice to either use the reference device
  784.         /// or to change to a back buffer format to compatible with the hardware device.  The match options lets the 
  785.         /// caller control how these choices are made.
  786.         /// 
  787.         /// Each match option must be one of the following types: 
  788.         /// MatchType.IgnoreInput: Uses the closest valid value to a default 
  789.         /// MatchType.PreserveInput: Uses the input without change, but may cause no valid device to be found
  790.         /// MatchType.ClosestToInput: Uses the closest valid value to the input 
  791.         /// </summary>
  792.         private DeviceSettings FindValidDeviceSettings(DeviceSettings settings, MatchOptions match)
  793.         {
  794.             // Build an optimal device settings structure based upon the match 
  795.             // options.  If the match option is set to ignore, then a optimal default value is used.
  796.             // The default value may not exist on the system, but later this will be taken 
  797.             // into account.
  798.             DeviceSettings optimalSettings = BuildOptimalDeviceSettings(settings, match);
  799.             float bestRanking = -1.0f;
  800.             EnumDeviceSettingsCombo bestDeviceSettingsCombo = new EnumDeviceSettingsCombo();
  801.  
  802.             // Find the best combination of:
  803.             //      Adapter Ordinal
  804.             //      Device Type
  805.             //      Adapter Format
  806.             //      Back Buffer Format
  807.             //      Windowed
  808.             // given what's available on the system and the match options combined with the device settings input.
  809.             // This combination of settings is encapsulated by the EnumDeviceSettingsCombo class.
  810.             DisplayMode adapterDesktopDisplayMode;
  811.             for (int iAdapter = 0; iAdapter < Enumeration.AdapterInformationList.Count; iAdapter++)
  812.             {
  813.                 EnumAdapterInformation adapterInfo = Enumeration.AdapterInformationList[iAdapter] as EnumAdapterInformation;
  814.                 
  815.                 // Get the desktop display mode of the adapter
  816.                 adapterDesktopDisplayMode = Manager.Adapters[(int)adapterInfo.AdapterOrdinal].CurrentDisplayMode;
  817.  
  818.                 // Enum all the device types supported by this adapter to find the best device settings
  819.                 for (int iDeviceInfo = 0; iDeviceInfo < adapterInfo.deviceInfoList.Count; iDeviceInfo++)
  820.                 {
  821.                     EnumDeviceInformation deviceInfo = adapterInfo.deviceInfoList[iDeviceInfo] as EnumDeviceInformation;
  822.                     for (int iDeviceCombo = 0; iDeviceCombo<deviceInfo.deviceSettingsList.Count; iDeviceCombo++)
  823.                     {
  824.                         EnumDeviceSettingsCombo deviceSettings = deviceInfo.deviceSettingsList[iDeviceCombo] as EnumDeviceSettingsCombo;
  825.                         // If windowed mode the adapter format has to be the same as the desktop 
  826.                         // display mode format so skip any that don't match
  827.                         if (deviceSettings.IsWindowed && (deviceSettings.AdapterFormat != adapterDesktopDisplayMode.Format))
  828.                             continue;
  829.  
  830.                         // Skip any combo that doesn't meet the preserve match options
  831.                         if(!DoesDeviceComboMatchPreserveOptions(deviceSettings, settings, match))
  832.                             continue;           
  833.  
  834.                         // Get a ranking number that describes how closely this device combo matches the optimal combo
  835.                         float curRanking = RankDeviceCombo(deviceSettings, optimalSettings, adapterDesktopDisplayMode);
  836.  
  837.                         // If this combo better matches the input device settings then save it
  838.                         if (curRanking > bestRanking )
  839.                         {
  840.                             bestDeviceSettingsCombo = deviceSettings;
  841.                             bestRanking = curRanking;
  842.                         }                
  843.                     }
  844.                 }
  845.             }
  846.  
  847.             // If no best device combination was found then fail
  848.             if (bestRanking == -1.0f)
  849.             {
  850.                 throw new NoCompatibleDevicesException();
  851.             }
  852.  
  853.             // Using the best device settings combo found, build valid device settings taking heed of 
  854.             // the match options and the input device settings
  855.             return BuildValidDeviceSettings(bestDeviceSettingsCombo, settings, match);
  856.         }
  857.         /// <summary>
  858.         /// Calls FindValidDeviceSettings with default match options (all ignore)
  859.         /// </summary>
  860.         private DeviceSettings FindValidDeviceSettings(DeviceSettings settings)
  861.         {
  862.             return FindValidDeviceSettings(settings, new MatchOptions());
  863.         }
  864.  
  865.         /// <summary>
  866.         /// public helper function to build a device settings structure based upon the match 
  867.         /// options.  If the match option is set to ignore, then a optimal default value is used.
  868.         /// The default value may not exist on the system, but later this will be taken 
  869.         /// into account.
  870.         /// </summary>
  871.         private DeviceSettings BuildOptimalDeviceSettings(DeviceSettings settings, MatchOptions match)
  872.         {
  873.             DeviceSettings optimal = new DeviceSettings(); // This will be what we return
  874.             optimal.presentParams = new PresentParameters();
  875.  
  876.             //---------------------
  877.             // Adapter ordinal
  878.             //---------------------    
  879.             if (match.AdapterOrdinal == MatchType.IgnoreInput)
  880.                 optimal.AdapterOrdinal = 0; 
  881.             else
  882.                 optimal.AdapterOrdinal = settings.AdapterOrdinal;      
  883.  
  884.             //---------------------
  885.             // Device type
  886.             //---------------------
  887.             if (match.DeviceType == MatchType.IgnoreInput)
  888.                 optimal.DeviceType = DeviceType.Hardware; 
  889.             else
  890.                 optimal.DeviceType = settings.DeviceType;
  891.  
  892.             //---------------------
  893.             // Windowed
  894.             //---------------------
  895.             if (match.Windowed == MatchType.IgnoreInput)
  896.                 optimal.presentParams.Windowed = true; 
  897.             else
  898.                 optimal.presentParams.Windowed = settings.presentParams.Windowed;
  899.  
  900.             //---------------------
  901.             // Adapter format
  902.             //---------------------
  903.             if (match.AdapterFormat == MatchType.IgnoreInput)
  904.             {
  905.                 // If windowed, default to the desktop display mode
  906.                 // If fullscreen, default to the desktop display mode for quick mode change or 
  907.                 // default to Format.X8R8G8B8 if the desktop display mode is < 32bit
  908.                 DisplayMode adapterDesktopMode = Manager.Adapters[(int)optimal.AdapterOrdinal].CurrentDisplayMode;
  909.  
  910.                 if (optimal.presentParams.Windowed || 
  911.                     ManagedUtility.GetColorChannelBits(adapterDesktopMode.Format) >= 8 )
  912.                     optimal.AdapterFormat = adapterDesktopMode.Format;
  913.                 else
  914.                     optimal.AdapterFormat = Format.X8R8G8B8;
  915.             }
  916.             else
  917.             {
  918.                 optimal.AdapterFormat = settings.AdapterFormat;
  919.             }
  920.  
  921.             //---------------------
  922.             // Vertex processing
  923.             //---------------------
  924.             if (match.VertexProcessing == MatchType.IgnoreInput)
  925.                 optimal.BehaviorFlags = CreateFlags.HardwareVertexProcessing; 
  926.             else
  927.                 optimal.BehaviorFlags = settings.BehaviorFlags;
  928.  
  929.             //---------------------
  930.             // Resolution
  931.             //---------------------
  932.             if (match.Resolution == MatchType.IgnoreInput)
  933.             {
  934.                 // If windowed, default to 640x480
  935.                 // If fullscreen, default to the desktop res for quick mode change
  936.                 if (optimal.presentParams.Windowed )
  937.                 {
  938.                     optimal.presentParams.BackBufferWidth = DefaultSizeWidth;
  939.                     optimal.presentParams.BackBufferHeight = DefaultSizeHeight;
  940.                 }
  941.                 else
  942.                 {
  943.                     DisplayMode adapterDesktopMode = Manager.Adapters[(int)optimal.AdapterOrdinal].CurrentDisplayMode;
  944.  
  945.                     optimal.presentParams.BackBufferWidth = adapterDesktopMode.Width;
  946.                     optimal.presentParams.BackBufferHeight = adapterDesktopMode.Height;
  947.                 }
  948.             }
  949.             else
  950.             {
  951.                 optimal.presentParams.BackBufferWidth = settings.presentParams.BackBufferWidth;
  952.                 optimal.presentParams.BackBufferHeight = settings.presentParams.BackBufferHeight;
  953.             }
  954.  
  955.             //---------------------
  956.             // Back buffer format
  957.             //---------------------
  958.             if (match.BackBufferFormat == MatchType.IgnoreInput)
  959.                 optimal.presentParams.BackBufferFormat = optimal.AdapterFormat; // Default to match the adapter format
  960.             else
  961.                 optimal.presentParams.BackBufferFormat = settings.presentParams.BackBufferFormat;
  962.  
  963.             //---------------------
  964.             // Back buffer count
  965.             //---------------------
  966.             if (match.BackBufferCount == MatchType.IgnoreInput)
  967.                 optimal.presentParams.BackBufferCount = 2; // Default to triple buffering for perf gain
  968.             else
  969.                 optimal.presentParams.BackBufferCount = settings.presentParams.BackBufferCount;
  970.    
  971.             //---------------------
  972.             // Multisample
  973.             //---------------------
  974.             if (match.MultiSample == MatchType.IgnoreInput)
  975.                 optimal.presentParams.MultiSampleQuality = 0; // Default to no multisampling 
  976.             else
  977.                 optimal.presentParams.MultiSampleQuality = settings.presentParams.MultiSampleQuality;
  978.  
  979.             //---------------------
  980.             // Swap effect
  981.             //---------------------
  982.             if (match.SwapEffect == MatchType.IgnoreInput)
  983.                 optimal.presentParams.SwapEffect = SwapEffect.Discard; 
  984.             else
  985.                 optimal.presentParams.SwapEffect = settings.presentParams.SwapEffect;
  986.  
  987.             //---------------------
  988.             // Depth stencil 
  989.             //---------------------
  990.             if (match.DepthFormat == MatchType.IgnoreInput &&
  991.                 match.StencilFormat == MatchType.IgnoreInput)
  992.             {
  993.                 uint backBufferBits = ManagedUtility.GetColorChannelBits(optimal.presentParams.BackBufferFormat);
  994.                 if (backBufferBits >= 8)
  995.                     optimal.presentParams.AutoDepthStencilFormat = DepthFormat.D32; 
  996.                 else
  997.                     optimal.presentParams.AutoDepthStencilFormat = DepthFormat.D16; 
  998.             }
  999.             else
  1000.             {
  1001.                 optimal.presentParams.AutoDepthStencilFormat = settings.presentParams.AutoDepthStencilFormat;
  1002.             }
  1003.  
  1004.             //---------------------
  1005.             // Present flags
  1006.             //---------------------
  1007.             if (match.PresentFlags == MatchType.IgnoreInput)
  1008.                 optimal.presentParams.PresentFlag = PresentFlag.DiscardDepthStencil;
  1009.             else
  1010.                 optimal.presentParams.PresentFlag = settings.presentParams.PresentFlag;
  1011.  
  1012.             //---------------------
  1013.             // Refresh rate
  1014.             //---------------------
  1015.             if (match.RefreshRate == MatchType.IgnoreInput)
  1016.                 optimal.presentParams.FullScreenRefreshRateInHz = 0;
  1017.             else
  1018.                 optimal.presentParams.FullScreenRefreshRateInHz = settings.presentParams.FullScreenRefreshRateInHz;
  1019.  
  1020.             //---------------------
  1021.             // Present interval
  1022.             //---------------------
  1023.             if (match.PresentInterval == MatchType.IgnoreInput)
  1024.             {
  1025.                 // For windowed, default to PresentInterval.Immediate
  1026.                 // which will wait not for the vertical retrace period to prevent tearing, 
  1027.                 // but may introduce tearing.
  1028.                 // For full screen, default to PresentInterval.Default 
  1029.                 // which will wait for the vertical retrace period to prevent tearing.
  1030.                 if (optimal.presentParams.Windowed )
  1031.                     optimal.presentParams.PresentationInterval = PresentInterval.Immediate;
  1032.                 else
  1033.                     optimal.presentParams.PresentationInterval = PresentInterval.Default;
  1034.             }
  1035.             else
  1036.             {
  1037.                 optimal.presentParams.PresentationInterval = settings.presentParams.PresentationInterval;
  1038.             }
  1039.  
  1040.             return optimal;
  1041.         }
  1042.  
  1043.  
  1044.         /// <summary>
  1045.         /// Builds valid device settings using the match options, the input device settings, and the 
  1046.         /// best device settings combo found.
  1047.         /// </summary>
  1048.         private DeviceSettings BuildValidDeviceSettings(EnumDeviceSettingsCombo deviceCombo, DeviceSettings settings, MatchOptions match)
  1049.         {
  1050.             DeviceSettings validSettings = new DeviceSettings();
  1051.             DisplayMode adapterDesktopDisplayMode = Manager.Adapters[(int)deviceCombo.AdapterOrdinal].CurrentDisplayMode;
  1052.  
  1053.             // For each setting pick the best, taking into account the match options and 
  1054.             // what's supported by the device
  1055.  
  1056.             //---------------------
  1057.             // Adapter Ordinal
  1058.             //---------------------
  1059.             // Just using deviceCombo.AdapterOrdinal
  1060.  
  1061.             //---------------------
  1062.             // Device Type
  1063.             //---------------------
  1064.             // Just using deviceCombo.DeviceType
  1065.  
  1066.             //---------------------
  1067.             // Windowed 
  1068.             //---------------------
  1069.             // Just using deviceCombo.Windowed
  1070.  
  1071.             //---------------------
  1072.             // Adapter Format
  1073.             //---------------------
  1074.             // Just using deviceCombo.AdapterFormat
  1075.  
  1076.             //---------------------
  1077.             // Vertex processing
  1078.             //---------------------
  1079.             CreateFlags bestBehaviorFlags = 0;
  1080.             if (match.VertexProcessing == MatchType.PreserveInput )   
  1081.             {
  1082.                 bestBehaviorFlags = settings.BehaviorFlags;
  1083.             }
  1084.             else if (match.VertexProcessing == MatchType.IgnoreInput )    
  1085.             {
  1086.                 // The framework defaults to HWVP if available otherwise use SWVP
  1087.                 if (deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight)
  1088.                     bestBehaviorFlags |= CreateFlags.HardwareVertexProcessing;
  1089.                 else
  1090.                     bestBehaviorFlags |= CreateFlags.SoftwareVertexProcessing;
  1091.             }
  1092.             else 
  1093.             {
  1094.                 // Default to input, and fallback to SWVP if HWVP not available 
  1095.                 bestBehaviorFlags = settings.BehaviorFlags;
  1096.                 if ((!deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight) && 
  1097.                     ( (bestBehaviorFlags & CreateFlags.HardwareVertexProcessing ) != 0 || 
  1098.                     (bestBehaviorFlags & CreateFlags.MixedVertexProcessing) != 0) )
  1099.                 {
  1100.                     bestBehaviorFlags &= ~CreateFlags.HardwareVertexProcessing ;
  1101.                     bestBehaviorFlags &= ~CreateFlags.MixedVertexProcessing;
  1102.                     bestBehaviorFlags |= CreateFlags.SoftwareVertexProcessing;
  1103.                 }
  1104.  
  1105.                 // One of these must be selected
  1106.                 if ((bestBehaviorFlags & CreateFlags.HardwareVertexProcessing ) == 0 &&
  1107.                     (bestBehaviorFlags & CreateFlags.MixedVertexProcessing) == 0 &&
  1108.                     (bestBehaviorFlags & CreateFlags.SoftwareVertexProcessing) == 0 )
  1109.                 {
  1110.                     if (deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight)
  1111.                         bestBehaviorFlags |= CreateFlags.HardwareVertexProcessing ;
  1112.                     else
  1113.                         bestBehaviorFlags |= CreateFlags.SoftwareVertexProcessing;
  1114.                 }
  1115.             }
  1116.  
  1117.             //---------------------
  1118.             // Resolution
  1119.             //---------------------
  1120.             DisplayMode bestDisplayMode = new DisplayMode();  
  1121.             if (match.Resolution == MatchType.PreserveInput )   
  1122.             {
  1123.                 bestDisplayMode.Width = settings.presentParams.BackBufferWidth;
  1124.                 bestDisplayMode.Height = settings.presentParams.BackBufferHeight;
  1125.             }
  1126.             else 
  1127.             {
  1128.                 DisplayMode displayModeIn = new DisplayMode();  
  1129.                 if (match.Resolution == MatchType.ClosestToInput &&
  1130.                     (settings.presentParams.BackBufferWidth != 0 && settings.presentParams.BackBufferWidth != 0) )   
  1131.                 {
  1132.                     displayModeIn.Width = settings.presentParams.BackBufferWidth;
  1133.                     displayModeIn.Height = settings.presentParams.BackBufferHeight;
  1134.                 }
  1135.                 else // if (match.Resolution == MatchType.IgnoreInput )   
  1136.                 {
  1137.                     if (deviceCombo.IsWindowed )
  1138.                     {
  1139.                         // The framework defaults to 640x480 for windowed
  1140.                         displayModeIn.Width = DefaultSizeWidth;
  1141.                         displayModeIn.Height = DefaultSizeHeight;
  1142.                     }
  1143.                     else
  1144.                     {
  1145.                         // The framework defaults to desktop resolution for fullscreen to try to avoid slow mode change
  1146.                         displayModeIn.Width = adapterDesktopDisplayMode.Width;
  1147.                         displayModeIn.Height = adapterDesktopDisplayMode.Height;
  1148.                     }
  1149.                 }
  1150.  
  1151.                 // Call a helper function to find the closest valid display mode to the optimal 
  1152.                 bestDisplayMode = FindValidResolution(deviceCombo, displayModeIn);
  1153.             }
  1154.  
  1155.             //---------------------
  1156.             // Back Buffer Format
  1157.             //---------------------
  1158.             // Just using deviceCombo.BackBufferFormat
  1159.  
  1160.             //---------------------
  1161.             // Back buffer count
  1162.             //---------------------
  1163.             uint bestBackBufferCount;
  1164.             if (match.BackBufferCount == MatchType.PreserveInput )   
  1165.             {
  1166.                 bestBackBufferCount = (uint)settings.presentParams.BackBufferCount;
  1167.             }
  1168.             else if (match.BackBufferCount == MatchType.IgnoreInput )   
  1169.             {
  1170.                 // The framework defaults to triple buffering 
  1171.                 bestBackBufferCount = 2;
  1172.             }
  1173.             else // if (match.BackBufferCount == MatchType.ClosestToInput )   
  1174.             {
  1175.                 bestBackBufferCount = (uint)settings.presentParams.BackBufferCount;
  1176.                 if (bestBackBufferCount > 3 )
  1177.                     bestBackBufferCount = 3;
  1178.                 if (bestBackBufferCount < 1 )
  1179.                     bestBackBufferCount = 1;
  1180.             }
  1181.     
  1182.             //---------------------
  1183.             // Multisample
  1184.             //---------------------
  1185.             MultiSampleType bestMultiSampleType;
  1186.             uint bestMultiSampleQuality;
  1187.             if (settings.presentParams.SwapEffect != SwapEffect.Discard)
  1188.             {
  1189.                 // Swap effect is not set to discard so multisampling has to off
  1190.                 bestMultiSampleType = MultiSampleType.None;
  1191.                 bestMultiSampleQuality = 0;
  1192.             }
  1193.             else
  1194.             {
  1195.                 if (match.BackBufferCount == MatchType.PreserveInput )   
  1196.                 {
  1197.                     bestMultiSampleType    = settings.presentParams.MultiSample;
  1198.                     bestMultiSampleQuality = (uint)settings.presentParams.MultiSampleQuality;
  1199.                 }
  1200.                 else if (match.BackBufferCount == MatchType.IgnoreInput )   
  1201.                 {
  1202.                     // Default to no multisampling (always supported)
  1203.                     bestMultiSampleType = MultiSampleType.None;
  1204.                     bestMultiSampleQuality = 0;
  1205.                 }
  1206.                 else // if (match.BackBufferCount == MatchType.ClosestToInput )   
  1207.                 {
  1208.                     if (deviceCombo.multiSampleTypeList.Count > 0 )
  1209.                     {
  1210.                         MultiSampleType highestSupportedMSType = (MultiSampleType)deviceCombo.multiSampleTypeList[deviceCombo.multiSampleTypeList.Count - 1];
  1211.                         if (settings.presentParams.MultiSample > highestSupportedMSType )
  1212.                         {
  1213.                             bestMultiSampleType = highestSupportedMSType;
  1214.                             bestMultiSampleQuality = 0;
  1215.                         }
  1216.                         else
  1217.                         {
  1218.                             uint highMultiSampleQuality = 0;
  1219.                             bestMultiSampleType = settings.presentParams.MultiSample;
  1220.                             for( int i=0; i<deviceCombo.multiSampleTypeList.Count; i++ )
  1221.                             {
  1222.                                 if ((MultiSampleType)deviceCombo.multiSampleTypeList[i] == bestMultiSampleType )
  1223.                                 {
  1224.                                     highMultiSampleQuality = (uint)(int)deviceCombo.multiSampleQualityList[i];
  1225.                                     break;
  1226.                                 }
  1227.                             }
  1228.  
  1229.                             if (settings.presentParams.MultiSampleQuality > highMultiSampleQuality )
  1230.                                 bestMultiSampleQuality = highMultiSampleQuality;
  1231.                             else
  1232.                                 bestMultiSampleQuality = (uint)settings.presentParams.MultiSampleQuality;
  1233.                         }
  1234.                     }
  1235.                     else
  1236.                     {
  1237.                         // Default to no multisampling (always supported)
  1238.                         bestMultiSampleType = MultiSampleType.None;
  1239.                         bestMultiSampleQuality = 0;
  1240.                     }
  1241.                 }
  1242.             }
  1243.  
  1244.             //---------------------
  1245.             // Swap effect
  1246.             //---------------------
  1247.             SwapEffect bestSwapEffect;
  1248.             if (match.SwapEffect == MatchType.PreserveInput )   
  1249.             {
  1250.                 bestSwapEffect = settings.presentParams.SwapEffect;
  1251.             }
  1252.             else if (match.SwapEffect == MatchType.IgnoreInput )   
  1253.             {
  1254.                 bestSwapEffect = SwapEffect.Discard;
  1255.             }
  1256.             else // if (match.SwapEffect == MatchType.ClosestToInput )   
  1257.             {
  1258.                 bestSwapEffect = settings.presentParams.SwapEffect;
  1259.  
  1260.                 // Swap effect has to be one of these 3
  1261.                 if (bestSwapEffect != SwapEffect.Discard &&
  1262.                     bestSwapEffect != SwapEffect.Flip &&
  1263.                     bestSwapEffect != SwapEffect.Copy )
  1264.                 {
  1265.                     bestSwapEffect = SwapEffect.Discard;
  1266.                 }
  1267.             }
  1268.  
  1269.             //---------------------
  1270.             // Depth stencil 
  1271.             //---------------------
  1272.             DepthFormat bestDepthStencilFormat;
  1273.             bool bestEnableAutoDepthStencil;
  1274.  
  1275.             int[] depthStencilRanking = new int[deviceCombo.depthStencilFormatList.Count];
  1276.  
  1277.             uint backBufferBitDepth = ManagedUtility.GetColorChannelBits( deviceCombo.BackBufferFormat );       
  1278.             uint inputDepthBitDepth = ManagedUtility.GetDepthBits( settings.presentParams.AutoDepthStencilFormat );
  1279.  
  1280.             for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ )
  1281.             {
  1282.                 DepthFormat curDepthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i];
  1283.                 uint curDepthBitDepth = ManagedUtility.GetDepthBits( curDepthStencilFmt );
  1284.                 int ranking;
  1285.  
  1286.                 if (match.DepthFormat == MatchType.PreserveInput )
  1287.                 {                       
  1288.                     // Need to match bit depth of input
  1289.                     if(curDepthBitDepth == inputDepthBitDepth)
  1290.                         ranking = 0;
  1291.                     else
  1292.                         ranking = 10000;
  1293.                 }
  1294.                 else if (match.DepthFormat == MatchType.IgnoreInput )
  1295.                 {
  1296.                     // Prefer match of backbuffer bit depth
  1297.                     ranking = Math.Abs((int)curDepthBitDepth - (int)(backBufferBitDepth*4));
  1298.                 }
  1299.                 else // if (match.DepthFormat == MatchType.ClosestToInput )
  1300.                 {
  1301.                     // Prefer match of input depth format bit depth
  1302.                     ranking = Math.Abs((int)curDepthBitDepth - (int)inputDepthBitDepth);
  1303.                 }
  1304.  
  1305.                 depthStencilRanking[i] = ranking;
  1306.             }
  1307.  
  1308.             uint inputStencilBitDepth = ManagedUtility.GetStencilBits( settings.presentParams.AutoDepthStencilFormat );
  1309.  
  1310.             for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ )
  1311.             {
  1312.                 DepthFormat curDepthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i];
  1313.                 int ranking = depthStencilRanking[i];
  1314.                 uint curStencilBitDepth = ManagedUtility.GetStencilBits( curDepthStencilFmt );
  1315.  
  1316.                 if (match.StencilFormat == MatchType.PreserveInput )
  1317.                 {                       
  1318.                     // Need to match bit depth of input
  1319.                     if(curStencilBitDepth == inputStencilBitDepth)
  1320.                         ranking += 0;
  1321.                     else
  1322.                         ranking += 10000;
  1323.                 }
  1324.                 else if (match.StencilFormat == MatchType.IgnoreInput )
  1325.                 {
  1326.                     // Prefer 0 stencil bit depth
  1327.                     ranking += (int)curStencilBitDepth;
  1328.                 }
  1329.                 else // if (match.StencilFormat == MatchType.ClosestToInput )
  1330.                 {
  1331.                     // Prefer match of input stencil format bit depth
  1332.                     ranking += Math.Abs((int)curStencilBitDepth - (int)inputStencilBitDepth);
  1333.                 }
  1334.  
  1335.                 depthStencilRanking[i] = ranking;
  1336.             }
  1337.  
  1338.             int bestRanking = 100000;
  1339.             int bestIndex = -1;
  1340.             for( int i=0; i<depthStencilRanking.Length; i++ )
  1341.             {
  1342.                 if (depthStencilRanking[i] < bestRanking )
  1343.                 {
  1344.                     bestRanking = depthStencilRanking[i];
  1345.                     bestIndex = i;
  1346.                 }
  1347.             }
  1348.  
  1349.             if (bestIndex >= 0 )
  1350.             {
  1351.                 bestDepthStencilFormat = (DepthFormat)deviceCombo.depthStencilFormatList[bestIndex];
  1352.                 bestEnableAutoDepthStencil = true;
  1353.             }
  1354.             else
  1355.             {
  1356.                 bestDepthStencilFormat = DepthFormat.Unknown;
  1357.                 bestEnableAutoDepthStencil = false;
  1358.             }
  1359.  
  1360.  
  1361.             //---------------------
  1362.             // Present flags
  1363.             //---------------------
  1364.             PresentFlag bestPresentFlag;
  1365.             if (match.PresentFlags == MatchType.PreserveInput )   
  1366.             {
  1367.                 bestPresentFlag = settings.presentParams.PresentFlag;
  1368.             }
  1369.             else if (match.PresentFlags == MatchType.IgnoreInput )   
  1370.             {
  1371.                 bestPresentFlag = 0;
  1372.                 if (bestEnableAutoDepthStencil )
  1373.                     bestPresentFlag = PresentFlag.DiscardDepthStencil;            
  1374.             }
  1375.             else // if (match.PresentFlags == MatchType.ClosestToInput )   
  1376.             {
  1377.                 bestPresentFlag = settings.presentParams.PresentFlag;
  1378.                 if (bestEnableAutoDepthStencil )
  1379.                     bestPresentFlag |= PresentFlag.DiscardDepthStencil;
  1380.             }
  1381.  
  1382.             //---------------------
  1383.             // Refresh rate
  1384.             //---------------------
  1385.             if (deviceCombo.IsWindowed )
  1386.             {
  1387.                 // Must be 0 for windowed
  1388.                 bestDisplayMode.RefreshRate = 0;
  1389.             }
  1390.             else
  1391.             {
  1392.                 if (match.RefreshRate == MatchType.PreserveInput )   
  1393.                 {
  1394.                     bestDisplayMode.RefreshRate = settings.presentParams.FullScreenRefreshRateInHz;
  1395.                 }
  1396.                 else 
  1397.                 {
  1398.                     uint refreshRateMatch;
  1399.                     if (match.RefreshRate == MatchType.ClosestToInput )   
  1400.                     {
  1401.                         refreshRateMatch = (uint)settings.presentParams.FullScreenRefreshRateInHz;
  1402.                     }
  1403.                     else // if (match.RefreshRate == MatchType.IgnoreInput )   
  1404.                     {
  1405.                         refreshRateMatch = (uint)adapterDesktopDisplayMode.RefreshRate;
  1406.                     }
  1407.  
  1408.                     bestDisplayMode.RefreshRate = 0;
  1409.  
  1410.                     if (refreshRateMatch != 0 )
  1411.                     {
  1412.                         int bestRefreshRanking = 100000;
  1413.                         for( int iDisplayMode=0; iDisplayMode<deviceCombo.adapterInformation.displayModeList.Count; iDisplayMode++ )
  1414.                         {
  1415.                             DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[iDisplayMode];                
  1416.                             if (displayMode.Format != deviceCombo.AdapterFormat || 
  1417.                                 displayMode.Height != bestDisplayMode.Height ||
  1418.                                 displayMode.Width != bestDisplayMode.Width )
  1419.                                 continue; // Skip display modes that don't match 
  1420.  
  1421.                             // Find the delta between the current refresh rate and the optimal refresh rate 
  1422.                             int currentRefreshRanking = Math.Abs((int)displayMode.RefreshRate - (int)refreshRateMatch);
  1423.                                         
  1424.                             if (currentRefreshRanking < bestRefreshRanking )
  1425.                             {
  1426.                                 bestDisplayMode.RefreshRate = displayMode.RefreshRate;
  1427.                                 bestRefreshRanking = currentRefreshRanking;
  1428.  
  1429.                                 // Stop if perfect match found
  1430.                                 if (bestRefreshRanking == 0 )
  1431.                                     break;
  1432.                             }
  1433.                         }
  1434.                     }
  1435.                 }
  1436.             }
  1437.  
  1438.             //---------------------
  1439.             // Present interval
  1440.             //---------------------
  1441.             PresentInterval bestPresentInterval;
  1442.             if (match.PresentInterval == MatchType.PreserveInput )   
  1443.             {
  1444.                 bestPresentInterval = settings.presentParams.PresentationInterval;
  1445.             }
  1446.             else if (match.PresentInterval == MatchType.IgnoreInput )   
  1447.             {
  1448.                 if (deviceCombo.IsWindowed )
  1449.                 {
  1450.                     // For windowed, the framework defaults to PresentInterval.Immediate
  1451.                     // which will wait not for the vertical retrace period to prevent tearing, 
  1452.                     // but may introduce tearing
  1453.                     bestPresentInterval = PresentInterval.Immediate;
  1454.                 }
  1455.                 else
  1456.                 {
  1457.                     // For full screen, the framework defaults to PresentInterval.Default 
  1458.                     // which will wait for the vertical retrace period to prevent tearing
  1459.                     bestPresentInterval = PresentInterval.Default;
  1460.                 }
  1461.             }
  1462.             else // if (match.PresentInterval == MatchType.ClosestToInput )   
  1463.             {
  1464.                 if (deviceCombo.presentIntervalList.Contains( settings.presentParams.PresentationInterval ) )
  1465.                 {
  1466.                     bestPresentInterval = settings.presentParams.PresentationInterval;
  1467.                 }
  1468.                 else
  1469.                 {
  1470.                     if (deviceCombo.IsWindowed )
  1471.                         bestPresentInterval = PresentInterval.Immediate;
  1472.                     else
  1473.                         bestPresentInterval = PresentInterval.Default;
  1474.                 }
  1475.             }
  1476.  
  1477.             // Fill the device settings struct
  1478.             validSettings.AdapterOrdinal = deviceCombo.AdapterOrdinal;
  1479.             validSettings.DeviceType = deviceCombo.DeviceType;
  1480.             validSettings.AdapterFormat = deviceCombo.AdapterFormat;
  1481.             validSettings.BehaviorFlags = bestBehaviorFlags;
  1482.             validSettings.presentParams = new PresentParameters();
  1483.             validSettings.presentParams.BackBufferWidth = bestDisplayMode.Width;
  1484.             validSettings.presentParams.BackBufferHeight = bestDisplayMode.Height;
  1485.             validSettings.presentParams.BackBufferFormat = deviceCombo.BackBufferFormat;
  1486.             validSettings.presentParams.BackBufferCount = (int)bestBackBufferCount;
  1487.             validSettings.presentParams.MultiSample = bestMultiSampleType;  
  1488.             validSettings.presentParams.MultiSampleQuality = (int)bestMultiSampleQuality;
  1489.             validSettings.presentParams.SwapEffect = bestSwapEffect;
  1490.             validSettings.presentParams.DeviceWindowHandle = deviceCombo.IsWindowed ? State.WindowDeviceWindowed : State.WindowDeviceFullScreen;
  1491.             validSettings.presentParams.Windowed = deviceCombo.IsWindowed;
  1492.             validSettings.presentParams.EnableAutoDepthStencil = bestEnableAutoDepthStencil;  
  1493.             validSettings.presentParams.AutoDepthStencilFormat = bestDepthStencilFormat;
  1494.             validSettings.presentParams.PresentFlag = bestPresentFlag;                   
  1495.             validSettings.presentParams.FullScreenRefreshRateInHz  = bestDisplayMode.RefreshRate;
  1496.             validSettings.presentParams.PresentationInterval = bestPresentInterval;
  1497.             validSettings.presentParams.ForceNoMultiThreadedFlag = true;
  1498.  
  1499.             return validSettings;
  1500.         }
  1501.  
  1502.         
  1503.         /// <summary>
  1504.         /// Returns false for any device combo that doesn't meet the preserve
  1505.         /// match options
  1506.         /// </summary>
  1507.         private bool DoesDeviceComboMatchPreserveOptions(EnumDeviceSettingsCombo deviceCombo, DeviceSettings settings, MatchOptions match)
  1508.         {
  1509.             //---------------------
  1510.             // Adapter ordinal
  1511.             //---------------------
  1512.             if (match.AdapterOrdinal == MatchType.PreserveInput && 
  1513.                 (deviceCombo.AdapterOrdinal != settings.AdapterOrdinal) )
  1514.                 return false;
  1515.  
  1516.             //---------------------
  1517.             // Device type
  1518.             //---------------------
  1519.             if (match.DeviceType == MatchType.PreserveInput && 
  1520.                 (deviceCombo.DeviceType != settings.DeviceType) )
  1521.                 return false;
  1522.  
  1523.             //---------------------
  1524.             // Windowed
  1525.             //---------------------
  1526.             if (match.Windowed == MatchType.PreserveInput && 
  1527.                 (deviceCombo.IsWindowed != settings.presentParams.Windowed) )
  1528.                 return false;
  1529.  
  1530.             //---------------------
  1531.             // Adapter format
  1532.             //---------------------
  1533.             if (match.AdapterFormat == MatchType.PreserveInput && 
  1534.                 (deviceCombo.AdapterFormat != settings.AdapterFormat) )
  1535.                 return false;
  1536.  
  1537.             //---------------------
  1538.             // Vertex processing
  1539.             //---------------------
  1540.             // If keep VP and input has HWVP, then skip if this combo doesn't have HWTL 
  1541.             if (match.VertexProcessing == MatchType.PreserveInput && 
  1542.                 ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0) && 
  1543.                 (deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight) )
  1544.                 return false;
  1545.  
  1546.             //---------------------
  1547.             // Resolution
  1548.             //---------------------
  1549.             // If keep resolution then check that width and height supported by this combo
  1550.             if (match.Resolution == MatchType.PreserveInput )
  1551.             {
  1552.                 bool bFound = false;
  1553.                 for( int i=0; i< deviceCombo.adapterInformation.displayModeList.Count; i++ )
  1554.                 {
  1555.                     DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[i];
  1556.                     if (displayMode.Format != deviceCombo.AdapterFormat )
  1557.                         continue; // Skip this display mode if it doesn't match the combo's adapter format
  1558.  
  1559.                     if (displayMode.Width == settings.presentParams.BackBufferWidth &&
  1560.                         displayMode.Height == settings.presentParams.BackBufferHeight )
  1561.                     {
  1562.                         bFound = true;
  1563.                         break;
  1564.                     }
  1565.                 }
  1566.  
  1567.                 // If the width and height are not supported by this combo, return false
  1568.                 if (!bFound )
  1569.                     return false;
  1570.             }
  1571.  
  1572.             //---------------------
  1573.             // Back buffer format
  1574.             //---------------------
  1575.             if (match.BackBufferFormat == MatchType.PreserveInput && 
  1576.                 deviceCombo.BackBufferFormat != settings.presentParams.BackBufferFormat )
  1577.                 return false;
  1578.  
  1579.             //---------------------
  1580.             // Back buffer count
  1581.             //---------------------
  1582.             // No caps for the back buffer count
  1583.  
  1584.             //---------------------
  1585.             // Multisample
  1586.             //---------------------
  1587.             if (match.MultiSample == MatchType.PreserveInput )
  1588.             {
  1589.                 bool bFound = false;
  1590.                 for( int i=0; i<deviceCombo.multiSampleTypeList.Count; i++ )
  1591.                 {
  1592.                     MultiSampleType msType = (MultiSampleType)deviceCombo.multiSampleTypeList[i];
  1593.                     uint msQuality  = (uint)(int)deviceCombo.multiSampleQualityList[i];
  1594.  
  1595.                     if (msType == settings.presentParams.MultiSample &&
  1596.                         msQuality >= settings.presentParams.MultiSampleQuality )
  1597.                     {
  1598.                         bFound = true;
  1599.                         break;
  1600.                     }
  1601.                 }
  1602.  
  1603.                 // If multisample type/quality not supported by this combo, then return false
  1604.                 if (!bFound )
  1605.                     return false;
  1606.             }
  1607.         
  1608.             //---------------------
  1609.             // Swap effect
  1610.             //---------------------
  1611.             // No caps for swap effects
  1612.  
  1613.             //---------------------
  1614.             // Depth stencil 
  1615.             //---------------------
  1616.             // If keep depth stencil format then check that the depth stencil format is supported by this combo
  1617.             if (match.DepthFormat == MatchType.PreserveInput &&
  1618.                 match.StencilFormat == MatchType.PreserveInput )
  1619.             {
  1620.                 if (settings.presentParams.AutoDepthStencilFormat != (DepthFormat)Format.Unknown &&
  1621.                     !deviceCombo.depthStencilFormatList.Contains( settings.presentParams.AutoDepthStencilFormat ) )
  1622.                     return false;
  1623.             }
  1624.  
  1625.             // If keep depth format then check that the depth format is supported by this combo
  1626.             if (match.DepthFormat == MatchType.PreserveInput &&
  1627.                 settings.presentParams.AutoDepthStencilFormat != DepthFormat.Unknown )
  1628.             {
  1629.                 bool bFound = false;
  1630.                 uint depthBits = ManagedUtility.GetDepthBits( settings.presentParams.AutoDepthStencilFormat );
  1631.                 for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ )
  1632.                 {
  1633.                     DepthFormat depthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i];
  1634.                     uint curDepthBits = ManagedUtility.GetDepthBits(depthStencilFmt);
  1635.                     if (curDepthBits - depthBits == 0)
  1636.                         bFound = true;
  1637.                 }
  1638.  
  1639.                 if (!bFound )
  1640.                     return false;
  1641.             }
  1642.  
  1643.             // If keep depth format then check that the depth format is supported by this combo
  1644.             if (match.StencilFormat == MatchType.PreserveInput &&
  1645.                 settings.presentParams.AutoDepthStencilFormat != DepthFormat.Unknown )
  1646.             {
  1647.                 bool bFound = false;
  1648.                 uint stencilBits = ManagedUtility.GetStencilBits( settings.presentParams.AutoDepthStencilFormat );
  1649.                 for( int i=0; i<deviceCombo.depthStencilFormatList.Count; i++ )
  1650.                 {
  1651.                     DepthFormat depthStencilFmt = (DepthFormat)deviceCombo.depthStencilFormatList[i];
  1652.                     uint curStencilBits = ManagedUtility.GetStencilBits(depthStencilFmt);
  1653.                     if (curStencilBits - stencilBits == 0)
  1654.                         bFound = true;
  1655.                 }
  1656.  
  1657.                 if (!bFound )
  1658.                     return false;
  1659.             }
  1660.  
  1661.             //---------------------
  1662.             // Present flags
  1663.             //---------------------
  1664.             // No caps for the present flags
  1665.  
  1666.             //---------------------
  1667.             // Refresh rate
  1668.             //---------------------
  1669.             // If keep refresh rate then check that the resolution is supported by this combo
  1670.             if (match.RefreshRate == MatchType.PreserveInput )
  1671.             {
  1672.                 bool bFound = false;
  1673.                 for( int i=0; i<deviceCombo.adapterInformation.displayModeList.Count; i++ )
  1674.                 {
  1675.                     DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[i];
  1676.                     if (displayMode.Format != deviceCombo.AdapterFormat )
  1677.                         continue;
  1678.                     if (displayMode.RefreshRate == settings.presentParams.FullScreenRefreshRateInHz )
  1679.                     {
  1680.                         bFound = true;
  1681.                         break;
  1682.                     }
  1683.                 }
  1684.  
  1685.                 // If refresh rate not supported by this combo, then return false
  1686.                 if (!bFound )
  1687.                     return false;
  1688.             }
  1689.  
  1690.             //---------------------
  1691.             // Present interval
  1692.             //---------------------
  1693.             // If keep present interval then check that the present interval is supported by this combo
  1694.             if (match.PresentInterval == MatchType.PreserveInput &&
  1695.                 !deviceCombo.presentIntervalList.Contains( settings.presentParams.PresentationInterval ) )
  1696.                 return false;
  1697.  
  1698.             return true;
  1699.         }
  1700.  
  1701.         /// <summary>
  1702.         /// Arbitrarily ranks device combo's
  1703.         /// </summary>
  1704.         private float RankDeviceCombo(EnumDeviceSettingsCombo deviceCombo, DeviceSettings settings, DisplayMode adapterMode)
  1705.         {
  1706.             float currentRanking = 0.0f; 
  1707.  
  1708.             // Arbitrary weights.  Gives preference to the ordinal, device type, and windowed
  1709.             const float adapterOrdinalWeight = 1000.0f;
  1710.             const float deviceTypeWeight = 100.0f;
  1711.             const float windowWeight = 10.0f;
  1712.             const float adapterFormatWeight = 1.0f;
  1713.             const float vertexProcessingWeight = 1.0f;
  1714.             const float resolutionWeight = 1.0f;
  1715.             const float backBufferFormatWeight = 1.0f;
  1716.             const float multiSampleWeight = 1.0f;
  1717.             const float depthStencilWeight = 1.0f;
  1718.             const float refreshRateWeight = 1.0f;
  1719.             const float presentIntervalWeight = 1.0f;
  1720.  
  1721.  
  1722.             //---------------------
  1723.             // Adapter ordinal
  1724.             //---------------------
  1725.             if (deviceCombo.AdapterOrdinal == settings.AdapterOrdinal )
  1726.                 currentRanking += adapterOrdinalWeight;
  1727.  
  1728.             //---------------------
  1729.             // Device type
  1730.             //---------------------
  1731.             if (deviceCombo.DeviceType == settings.DeviceType )
  1732.                 currentRanking += deviceTypeWeight;
  1733.             // Slightly prefer HAL 
  1734.             if (deviceCombo.DeviceType == DeviceType.Hardware )
  1735.                 currentRanking += 0.1f; 
  1736.  
  1737.             //---------------------
  1738.             // Windowed
  1739.             //---------------------
  1740.             if (deviceCombo.IsWindowed == settings.presentParams.Windowed )
  1741.                 currentRanking += windowWeight;
  1742.  
  1743.             //---------------------
  1744.             // Adapter format
  1745.             //---------------------
  1746.             if (deviceCombo.AdapterFormat == settings.AdapterFormat )
  1747.             {
  1748.                 currentRanking += adapterFormatWeight;
  1749.             }
  1750.             else
  1751.             {
  1752.                 int bitDepthDelta = Math.Abs((int)ManagedUtility.GetColorChannelBits(deviceCombo.AdapterFormat) -
  1753.                     (int)ManagedUtility.GetColorChannelBits(settings.AdapterFormat) );
  1754.                 float scale = Math.Max(0.9f - (float)bitDepthDelta*0.2f, 0);
  1755.                 currentRanking += scale * adapterFormatWeight;
  1756.             }
  1757.  
  1758.             if (!deviceCombo.IsWindowed )
  1759.             {
  1760.                 // Slightly prefer when it matches the desktop format or is Format.X8R8G8B8
  1761.                 bool isAdapterOptimalMatch;
  1762.                 if (ManagedUtility.GetColorChannelBits(adapterMode.Format) >= 8 )
  1763.                     isAdapterOptimalMatch = (deviceCombo.AdapterFormat == adapterMode.Format);
  1764.                 else
  1765.                     isAdapterOptimalMatch = (deviceCombo.AdapterFormat == Format.X8R8G8B8);
  1766.  
  1767.                 if (isAdapterOptimalMatch )
  1768.                     currentRanking += 0.1f;
  1769.             }
  1770.  
  1771.             //---------------------
  1772.             // Vertex processing
  1773.             //---------------------
  1774.             if ((settings.BehaviorFlags & CreateFlags.HardwareVertexProcessing) != 0 || 
  1775.                 (settings.BehaviorFlags & CreateFlags.MixedVertexProcessing) != 0 )
  1776.             {
  1777.                 if(deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight)
  1778.                     currentRanking += vertexProcessingWeight;
  1779.             }
  1780.             // Slightly prefer HW T&L
  1781.             if(deviceCombo.deviceInformation.Caps.DeviceCaps.SupportsHardwareTransformAndLight)
  1782.                 currentRanking += 0.1f;
  1783.  
  1784.             //---------------------
  1785.             // Resolution
  1786.             //---------------------
  1787.             bool bResolutionFound = false;
  1788.             for( int idm = 0; idm < deviceCombo.adapterInformation.displayModeList.Count; idm++ )
  1789.             {
  1790.                 DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[idm];
  1791.                 if (displayMode.Format != deviceCombo.AdapterFormat )
  1792.                     continue;
  1793.                 if (displayMode.Width == settings.presentParams.BackBufferWidth &&
  1794.                     displayMode.Height == settings.presentParams.BackBufferHeight )
  1795.                     bResolutionFound = true;
  1796.             }
  1797.             if (bResolutionFound )
  1798.                 currentRanking += resolutionWeight;
  1799.  
  1800.             //---------------------
  1801.             // Back buffer format
  1802.             //---------------------
  1803.             if (deviceCombo.BackBufferFormat == settings.presentParams.BackBufferFormat )
  1804.             {
  1805.                 currentRanking += backBufferFormatWeight;
  1806.             }
  1807.             else
  1808.             {
  1809.                 int bitDepthDelta = Math.Abs((int)ManagedUtility.GetColorChannelBits(deviceCombo.BackBufferFormat) -
  1810.                     (int)ManagedUtility.GetColorChannelBits(settings.presentParams.BackBufferFormat) );
  1811.                 float scale = Math.Max(0.9f - (float)bitDepthDelta*0.2f, 0);
  1812.                 currentRanking += scale * backBufferFormatWeight;
  1813.             }
  1814.  
  1815.             // Check if this back buffer format is the same as 
  1816.             // the adapter format since this is preferred.
  1817.             bool bAdapterMatchesBB = (deviceCombo.BackBufferFormat == deviceCombo.AdapterFormat);
  1818.             if (bAdapterMatchesBB )
  1819.                 currentRanking += 0.1f;
  1820.  
  1821.             //---------------------
  1822.             // Back buffer count
  1823.             //---------------------
  1824.             // No caps for the back buffer count
  1825.  
  1826.             //---------------------
  1827.             // Multisample
  1828.             //---------------------
  1829.             bool bMultiSampleFound = false;
  1830.             for( int i=0; i<deviceCombo.multiSampleTypeList.Count; i++ )
  1831.             {
  1832.                 MultiSampleType msType = (MultiSampleType)deviceCombo.multiSampleTypeList[i];
  1833.                 uint msQuality  = (uint)(int)deviceCombo.multiSampleQualityList[i];
  1834.  
  1835.                 if (msType == settings.presentParams.MultiSample &&
  1836.                     msQuality >= settings.presentParams.MultiSampleQuality )
  1837.                 {
  1838.                     bMultiSampleFound = true;
  1839.                     break;
  1840.                 }
  1841.             }
  1842.             if (bMultiSampleFound )
  1843.                 currentRanking += multiSampleWeight;
  1844.         
  1845.             //---------------------
  1846.             // Swap effect
  1847.             //---------------------
  1848.             // No caps for swap effects
  1849.  
  1850.             //---------------------
  1851.             // Depth stencil 
  1852.             //---------------------
  1853.             if (deviceCombo.depthStencilFormatList.Contains( settings.presentParams.AutoDepthStencilFormat ) )
  1854.                 currentRanking += depthStencilWeight;
  1855.  
  1856.             //---------------------
  1857.             // Present flags
  1858.             //---------------------
  1859.             // No caps for the present flags
  1860.  
  1861.             //---------------------
  1862.             // Refresh rate
  1863.             //---------------------
  1864.             bool bRefreshFound = false;
  1865.             for( int idm = 0; idm < deviceCombo.adapterInformation.displayModeList.Count; idm++ )
  1866.             {
  1867.                 DisplayMode displayMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[idm];
  1868.                 if (displayMode.Format != deviceCombo.AdapterFormat )
  1869.                     continue;
  1870.                 if (displayMode.RefreshRate == settings.presentParams.FullScreenRefreshRateInHz )
  1871.                     bRefreshFound = true;
  1872.             }
  1873.             if (bRefreshFound )
  1874.                 currentRanking += refreshRateWeight;
  1875.  
  1876.             //---------------------
  1877.             // Present interval
  1878.             //---------------------
  1879.             // If keep present interval then check that the present interval is supported by this combo
  1880.             if (deviceCombo.presentIntervalList.Contains( settings.presentParams.PresentationInterval ) )
  1881.                 currentRanking += presentIntervalWeight;
  1882.  
  1883.             return currentRanking;
  1884.         }
  1885.  
  1886.         /// <summary>
  1887.         /// public helper function to find the closest allowed display mode to the optimal 
  1888.         /// </summary>
  1889.         private DisplayMode FindValidResolution(EnumDeviceSettingsCombo deviceCombo, DisplayMode displayMode)
  1890.         {
  1891.             DisplayMode bestDisplayMode = displayMode;
  1892.  
  1893.             if (deviceCombo.IsWindowed)
  1894.             {
  1895.                 // Get the desktop resolution of the current monitor to use to keep the window
  1896.                 // in a reasonable size in the desktop's 
  1897.                 // This isn't the same as the current resolution from GetAdapterDisplayMode
  1898.                 // since the device might be fullscreen
  1899.                 EnumAdapterInformation adapterInfo = Enumeration.GetAdapterInformation(deviceCombo.AdapterOrdinal);
  1900.                 // Find the right screen
  1901.                 System.Windows.Forms.Screen displayScreen = null;
  1902.                 string adapterName = adapterInfo.AdapterInformation.DeviceName.ToLower();
  1903.                 foreach(System.Windows.Forms.Screen s in System.Windows.Forms.Screen.AllScreens)
  1904.                 {
  1905.                     string deviceName = s.DeviceName.ToLower();
  1906.                     // For some reason the device name has null characters in it.  Remove them
  1907.                     if (deviceName.IndexOf("\0") > 0)
  1908.                     {
  1909.                         deviceName = deviceName.Substring(0, deviceName.IndexOf("\0"));
  1910.                     }
  1911.                     if (deviceName == adapterName)
  1912.                     {
  1913.                         // Found the correct screen
  1914.                         displayScreen = s;
  1915.                         break;
  1916.                     }
  1917.                 }
  1918.                 System.Diagnostics.Debug.Assert(displayScreen != null, "We should have found the screen by now.");
  1919.  
  1920.                 // For windowed mode, just keep it something reasonable within the size 
  1921.                 // of the working area of the desktop
  1922.                 if (bestDisplayMode.Width > displayScreen.WorkingArea.Width - 20 )
  1923.                     bestDisplayMode.Width = displayScreen.WorkingArea.Width - 20;
  1924.                 if (bestDisplayMode.Height > displayScreen.WorkingArea.Height - 100 )
  1925.                     bestDisplayMode.Height = displayScreen.WorkingArea.Height - 100;
  1926.  
  1927.             }
  1928.             else
  1929.             {
  1930.                 int bestRanking = 100000;
  1931.                 int currentRanking;
  1932.                 for( int iDisplayMode=0; iDisplayMode<deviceCombo.adapterInformation.displayModeList.Count; iDisplayMode++ )
  1933.                 {
  1934.                     DisplayMode storedMode = (DisplayMode)deviceCombo.adapterInformation.displayModeList[iDisplayMode];
  1935.  
  1936.                     // Skip display modes that don't match the combo's adapter format
  1937.                     if (storedMode.Format != deviceCombo.AdapterFormat )
  1938.                         continue;
  1939.  
  1940.                     // Find the delta between the current width/height and the optimal width/height
  1941.                     currentRanking = Math.Abs((int)storedMode.Width - (int)displayMode.Width) + 
  1942.                         Math.Abs((int)storedMode.Height- (int)displayMode.Height);
  1943.  
  1944.                     if (currentRanking < bestRanking )
  1945.                     {
  1946.                         bestDisplayMode = displayMode;
  1947.                         bestRanking = currentRanking;
  1948.  
  1949.                         // Stop if perfect match found
  1950.                         if (bestRanking == 0 )
  1951.                             break;
  1952.                     }
  1953.                 }
  1954.  
  1955.                 // Were any found?
  1956.                 if (bestDisplayMode.Width == 0 )
  1957.                 {
  1958.                     throw new NoCompatibleDevicesException();
  1959.                 }
  1960.             }
  1961.  
  1962.             return bestDisplayMode;
  1963.         }
  1964.  
  1965.         /// <summary>
  1966.         /// Change to a Direct3D device created from the device settings or passed in.
  1967.         /// The framework will only reset if the device is similar to the previous device 
  1968.         /// otherwise it will cleanup the previous device (if there is one) and recreate the 
  1969.         /// scene using the app's device callbacks.
  1970.         /// </summary>
  1971.         private void ChangeDevice(DeviceSettings newDeviceSettings, Device deviceFromApp, bool forceRecreate)
  1972.         {
  1973.             // First store the current device settings
  1974.             DeviceSettings oldSettings = null;
  1975.             if (State.CurrentDeviceSettings != null)
  1976.                 oldSettings = State.CurrentDeviceSettings.Clone();
  1977.  
  1978.             // Pause the application
  1979.             Pause(true, true);
  1980.  
  1981.             // When a size message is received, it calls HandlePossibleSizeChange().
  1982.             // A size message might be sent when adjusting the window, so tell 
  1983.             // HandlePossibleSizeChange() to ignore size changes temporarily
  1984.             State.AreSizeChangesIgnored = true;
  1985.  
  1986.             // Only apply the cmd line overrides if this is the first device created
  1987.             // and SetDevice() isn't used
  1988.             if ( (deviceFromApp == null) && (oldSettings == null) )
  1989.             {
  1990.                 // Updates the device settings struct based on the cmd line args.  
  1991.                 // Warning: if the device doesn't support these new settings then CreateDevice() will fail.
  1992.                 UpdateDeviceSettingsWithOverrides(ref newDeviceSettings);
  1993.             }
  1994.  
  1995.             // If windowed, then update the window client rect and window bounds rect
  1996.             // with the new pp.BackBufferWidth & pp.BackBufferHeight
  1997.             // The window will be resized after the device is reset/creeted
  1998.             if (newDeviceSettings.presentParams.Windowed)
  1999.             {
  2000.                 // Don't allow smaller than what's used in minimum window size
  2001.                 // otherwise the window size will be different than the backbuffer size
  2002.                 if (newDeviceSettings.presentParams.BackBufferWidth < MinimumWindowSizeX)
  2003.                     newDeviceSettings.presentParams.BackBufferWidth = MinimumWindowSizeX;
  2004.                 if (newDeviceSettings.presentParams.BackBufferHeight < MinimumWindowSizeY)
  2005.                     newDeviceSettings.presentParams.BackBufferHeight = MinimumWindowSizeY;
  2006.  
  2007.                 // Create a new client rectangle of the correct size
  2008.                 System.Drawing.Rectangle windowClient = State.ClientRectangle;
  2009.                 windowClient.Size = new System.Drawing.Size(newDeviceSettings.presentParams.BackBufferWidth, newDeviceSettings.presentParams.BackBufferHeight);
  2010.  
  2011.                 // Adjust the window rect
  2012.                 NativeMethods.AdjustWindowRect(ref windowClient, State.WindowStyle, (State.Menu != null));
  2013.                 windowClient.Location = System.Drawing.Point.Empty;
  2014.  
  2015.                 State.ClientRectangle = windowClient; // Store this for resizing later
  2016.             }
  2017.  
  2018.             // Set the new device settings
  2019.             State.CurrentDeviceSettings = newDeviceSettings;
  2020.  
  2021.  
  2022.             // If AdapterOrdinal and DeviceType are the same, we can just do a Reset().
  2023.             // If they've changed, we need to do a complete device tear down/rebuild.
  2024.             // Also only allow a reset if deviceFromApp is the same as the current device 
  2025.             if( !forceRecreate && 
  2026.                 (deviceFromApp == null || deviceFromApp == State.Device) && 
  2027.                 oldSettings != null &&
  2028.                 oldSettings.AdapterOrdinal == newDeviceSettings.AdapterOrdinal &&
  2029.                 oldSettings.DeviceType == newDeviceSettings.DeviceType &&
  2030.                 oldSettings.BehaviorFlags == newDeviceSettings.BehaviorFlags )
  2031.             {
  2032.                 try
  2033.                 {
  2034.                     // Reset the Direct3D device 
  2035.                     Reset3DEnvironment();
  2036.                 }
  2037.                 catch
  2038.                 {
  2039.                     // The reset has failed, the device is lost
  2040.                     Pause (false, false);
  2041.                     State.IsDeviceLost = true;
  2042.                 }
  2043.             }
  2044.             else
  2045.             {
  2046.                 // Recreate the device
  2047.                 if (oldSettings != null)
  2048.                 {
  2049.                     // The adapter and device type don't match so 
  2050.                     // cleanup and create the 3D device again
  2051.                     Cleanup3DEnvironment(false);
  2052.                 }
  2053.                 
  2054.                 Device device = deviceFromApp;
  2055.                 // Only create a Direct3D device if one hasn't been supplied by the app
  2056.                 if (deviceFromApp == null)
  2057.                 {
  2058.                     // Turn off event handling for the MDX runtime, this will
  2059.                     // increase performance and remove potential working set issues
  2060.                     Device.IsUsingEventHandlers = false;
  2061.                     try
  2062.                     {
  2063.                         device = new Device((int)newDeviceSettings.AdapterOrdinal, newDeviceSettings.DeviceType,
  2064.                             State.WindowFocus, newDeviceSettings.BehaviorFlags,
  2065.                             newDeviceSettings.presentParams);
  2066.  
  2067.                         // Hook the device lost/reset events
  2068.                         device.DeviceLost += new EventHandler(OnDeviceLost);
  2069.                         device.DeviceReset += new EventHandler(OnDeviceRest);
  2070.                     }
  2071.                     catch (Exception e)
  2072.                     {
  2073.                         // There was an error creating the device
  2074.                         Pause(false, false);
  2075.                         CreatingDeviceException cde = new CreatingDeviceException(e);
  2076.                         DisplayErrorMessage(cde);
  2077.                         throw;
  2078.                     }
  2079.                 }
  2080.                 // Store the device
  2081.                 State.Device = device;
  2082.  
  2083.                 // Now that the device is created, update the window and misc settings and
  2084.                 // call the app's DeviceCreated and DeviceReset callbacks.
  2085.                 try
  2086.                 {
  2087.                     Initialize3DEnvironment();
  2088.                 }
  2089.                 catch
  2090.                 {
  2091.                     Pause(false, false);
  2092.                     throw;
  2093.                 }
  2094.                 // Update the device stats text
  2095.                 EnumAdapterInformation adapterInfo = Enumeration.GetAdapterInformation(newDeviceSettings.AdapterOrdinal);
  2096.                 UpdateDeviceStats(newDeviceSettings.DeviceType, newDeviceSettings.BehaviorFlags, adapterInfo.AdapterInformation);
  2097.             }
  2098.  
  2099.             // Get the adapter monitor
  2100.             State.AdapterMonitor = Manager.GetAdapterMonitor((int)newDeviceSettings.AdapterOrdinal);
  2101.  
  2102.             // When moving from full screen to windowed mode, it is important to
  2103.             // adjust the window size after resetting the device rather than
  2104.             // beforehand to ensure that you get the window size you want.  For
  2105.             // example, when switching from 640x480 full screen to windowed with
  2106.             // a 1000x600 window on a 1024x768 desktop, it is impossible to set
  2107.             // the window size to 1000x600 until after the display mode has
  2108.             // changed to 1024x768, because windows cannot be larger than the
  2109.             // desktop.
  2110.             if (newDeviceSettings.presentParams.Windowed)
  2111.             {
  2112.                 System.Drawing.Rectangle rect = State.WindowBoundsRectangle;
  2113.  
  2114.                 // Resize the window
  2115.                 System.Drawing.Point pt = rect.Location;
  2116.                 NativeMethods.ScreenToClient(NativeMethods.GetParent(Window), ref pt);
  2117.  
  2118.                 NativeMethods.SetWindowPos(Window, new IntPtr(-2), pt.X, pt.Y,
  2119.                     rect.Width - rect.Left,rect.Height - rect.Top, 0);
  2120.  
  2121.                 // Check to see if the monitor has changed windows
  2122.                 NativeMethods.MonitorInformation infoAdapter = new NativeMethods.MonitorInformation();
  2123.                 // Set size
  2124.                 infoAdapter.Size = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.MonitorInformation));
  2125.                 NativeMethods.GetMonitorInfo(State.AdapterMonitor, ref infoAdapter);
  2126.                 // Calculate width/height, this needs to happen because the unmanaged RECT structure is different than the managed Rectangle
  2127.                 int monitorWidth = infoAdapter.WorkRectangle.Right - infoAdapter.WorkRectangle.Left;
  2128.                 int monitorHeight = infoAdapter.WorkRectangle.Bottom - infoAdapter.WorkRectangle.Top;
  2129.  
  2130.                 // Get the monitor this app is on
  2131.                 IntPtr monitorHandle = NativeMethods.MonitorFromWindow(Window, 1); // Default to primary
  2132.                 NativeMethods.MonitorInformation infoWindow = new NativeMethods.MonitorInformation();
  2133.                 // Set size
  2134.                 infoWindow.Size = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(NativeMethods.MonitorInformation));
  2135.                 NativeMethods.GetMonitorInfo(monitorHandle, ref infoWindow);
  2136.  
  2137.                 rect = State.WindowBoundsRectangle;
  2138.                 int windowOffsetX = rect.Left - infoWindow.MonitorRectangle.Left;
  2139.                 int windowOffsetY = rect.Top - infoWindow.MonitorRectangle.Top;
  2140.                 int windowWidth = rect.Right - rect.Left;
  2141.                 int windowHeight = rect.Bottom - rect.Top;
  2142.  
  2143.                 if (State.IsWindowCreatedWithDefaultPositions)
  2144.                 {
  2145.                     // Since the window was created with a default window position
  2146.                     // center it in the work area if its outside the monitor's work area
  2147.  
  2148.                     // Only do this the first time.
  2149.                     State.IsWindowCreatedWithDefaultPositions = false;
  2150.  
  2151.                     // Center window if the bottom or right of the window is outside the monitor's work area
  2152.                     if (infoAdapter.WorkRectangle.Left + windowOffsetX + windowWidth > infoAdapter.WorkRectangle.Right)
  2153.                         windowOffsetX = (monitorWidth - windowWidth) / 2;
  2154.                     if (infoAdapter.WorkRectangle.Top + windowOffsetY + windowHeight > infoAdapter.WorkRectangle.Bottom)
  2155.                         windowOffsetY = (monitorHeight - windowHeight) / 2;
  2156.                 }
  2157.  
  2158.                 // Move & show the window 
  2159.                 pt.X = infoAdapter.MonitorRectangle.Left + windowOffsetX;
  2160.                 pt.Y = infoAdapter.MonitorRectangle.Top + windowOffsetY;
  2161.  
  2162.                 NativeMethods.ScreenToClient(NativeMethods.GetParent(Window), ref pt);
  2163.                 NativeMethods.SetWindowPos(Window, new IntPtr(-2), pt.X, pt.Y, 0, 0, 0x0040 | 1); // No Size
  2164.  
  2165.                 // Save the window position & size 
  2166.                 NativeMethods.GetClientRect(WindowDeviceWindowed, out rect);
  2167.                 State.ClientRectangle = rect;
  2168.                 NativeMethods.GetWindowRect(WindowDeviceWindowed, out rect);
  2169.                 State.WindowBoundsRectangle = rect;
  2170.  
  2171.             }
  2172.             else
  2173.             {
  2174.                 // Just store the full screen client rectangle
  2175.                 State.FullScreenClientRectangle = new System.Drawing.Rectangle(0,0, newDeviceSettings.presentParams.BackBufferWidth,
  2176.                     newDeviceSettings.presentParams.BackBufferHeight);
  2177.             }
  2178.  
  2179.             // Done creating the device
  2180.             State.AreSizeChangesIgnored = false;
  2181.             Pause(false, false);
  2182.             State.WasDeviceCreated = true;
  2183.         }
  2184.  
  2185.         /// <summary>
  2186.         /// Updates the string which describes the device 
  2187.         /// </summary>
  2188.         private void UpdateDeviceStats(DeviceType deviceType, CreateFlags behaviorFlags, AdapterDetails info)
  2189.         {
  2190.             // Get the behavior flags
  2191.             BehaviorFlags flags = new BehaviorFlags(behaviorFlags);
  2192.             // Store device information
  2193.             System.Text.StringBuilder builder = new System.Text.StringBuilder();
  2194.             // Append device type
  2195.             builder.Append(deviceType.ToString());
  2196.             // Append other types based on flags
  2197.             if (flags.HardwareVertexProcessing && flags.PureDevice)
  2198.             {
  2199.                 if (deviceType == DeviceType.Hardware)
  2200.                     builder.Append(" (pure hw vp)");
  2201.                 else
  2202.                     builder.Append(" (simulated pure hw vp)");
  2203.             }
  2204.             else if (flags.HardwareVertexProcessing)
  2205.             {
  2206.                 if (deviceType == DeviceType.Hardware)
  2207.                     builder.Append(" (hw vp)");
  2208.                 else
  2209.                     builder.Append(" (simulated hw vp)");
  2210.             }
  2211.             else if (flags.MixedVertexProcessing)
  2212.             {
  2213.                 if (deviceType == DeviceType.Hardware)
  2214.                     builder.Append(" (mixed vp)");
  2215.                 else
  2216.                     builder.Append(" (simulated mixed vp)");
  2217.             }
  2218.             else if (flags.SoftwareVertexProcessing)
  2219.             {
  2220.                 builder.Append(" (sw vp)");
  2221.             }
  2222.  
  2223.             // now add the description
  2224.             builder.Append(": ");
  2225.             builder.Append(info.Description);
  2226.  
  2227.             // Store the device stats string
  2228.             State.DeviceStats = builder.ToString();
  2229.         }
  2230.  
  2231.         /// <summary>
  2232.         /// Updates the device settings struct based on the cmd line args.  
  2233.         /// </summary>
  2234.         private void UpdateDeviceSettingsWithOverrides(ref DeviceSettings settings)
  2235.         {
  2236.             if (State.OverrideAdapterOrdinal != -1)
  2237.                 settings.AdapterOrdinal = (uint)State.OverrideAdapterOrdinal;
  2238.  
  2239.             if (State.IsOverridingFullScreen)
  2240.                 settings.presentParams.Windowed = false;
  2241.             if (State.IsOverridingWindowed)
  2242.                 settings.presentParams.Windowed = true;
  2243.  
  2244.             if (State.IsOverridingForceReference)
  2245.                 settings.DeviceType = DeviceType.Reference;
  2246.             else if (State.IsOverridingForceHardware)
  2247.                 settings.DeviceType = DeviceType.Hardware;
  2248.  
  2249.             if (State.OverrideWidth != 0)
  2250.                 settings.presentParams.BackBufferWidth = State.OverrideWidth;
  2251.             if (State.OverrideHeight != 0)
  2252.                 settings.presentParams.BackBufferHeight = State.OverrideHeight;
  2253.  
  2254.             if (State.IsOverridingForcePureHardwareVertexProcessing)
  2255.             {
  2256.                 settings.BehaviorFlags &= ~CreateFlags.SoftwareVertexProcessing;
  2257.                 settings.BehaviorFlags |= CreateFlags.HardwareVertexProcessing;
  2258.                 settings.BehaviorFlags |= CreateFlags.PureDevice;
  2259.             }
  2260.             else if (State.IsOverridingForceHardwareVertexProcessing)
  2261.             {
  2262.                 settings.BehaviorFlags &= ~CreateFlags.SoftwareVertexProcessing;
  2263.                 settings.BehaviorFlags &= ~CreateFlags.PureDevice;
  2264.                 settings.BehaviorFlags |= CreateFlags.HardwareVertexProcessing;
  2265.             }
  2266.             else if (State.IsOverridingForceSoftwareVertexProcessing)
  2267.             {
  2268.                 settings.BehaviorFlags &= ~CreateFlags.HardwareVertexProcessing;
  2269.                 settings.BehaviorFlags &= ~CreateFlags.PureDevice;
  2270.                 settings.BehaviorFlags |= CreateFlags.SoftwareVertexProcessing;
  2271.             }
  2272.         }
  2273.  
  2274.  
  2275.         /// <summary>
  2276.         /// Resets the 3D environment by:
  2277.         /// - Calls the device lost callback 
  2278.         /// - Resets the device
  2279.         /// - Stores the back buffer description
  2280.         /// - Sets up the full screen Direct3D cursor if requested
  2281.         /// - Calls the device reset callback 
  2282.         /// </summary>
  2283.         public void Reset3DEnvironment()
  2284.         {
  2285.             Device device = State.Device;
  2286.             System.Diagnostics.Debug.Assert(device != null, "The device should not be null here.");
  2287.             if (device == null)
  2288.                 throw new InvalidOperationException("The device should not be null.");
  2289.  
  2290.             // Fire the event if required
  2291.             if (DeviceLost != null)
  2292.                 DeviceLost(this, EventArgs.Empty);
  2293.  
  2294.             // Prepare the window for a possible change between windowed mode 
  2295.             // and full screen mode by adjusting the window style and its menu.
  2296.             AdjustWindowStyle(Window, IsWindowed);
  2297.  
  2298.             try
  2299.             {
  2300.                 // If the settings dialog exists call its OnResetDevice() 
  2301.                 // Reset the device
  2302.                 device.Reset(State.CurrentDeviceSettings.presentParams);
  2303.             }
  2304.             catch  (Exception e)
  2305.             {
  2306.                 if (e is MediaNotFoundException)
  2307.                     DisplayErrorMessage(e as MediaNotFoundException);
  2308.                 else
  2309.                     DisplayErrorMessage(new ResettingDeviceException(e));
  2310.  
  2311.                 // We failed, call the lost callback once more
  2312.                 OnDeviceLost(null, EventArgs.Empty);
  2313.                 throw;
  2314.             }
  2315.         }
  2316.  
  2317.         /// <summary>
  2318.         /// Render the 3D environment by:
  2319.         /// - Checking if the device is lost and trying to reset it if it is
  2320.         /// - Get the elapsed time since the last frame
  2321.         /// - Calling the app's framemove and render callback
  2322.         /// - Calling Present()
  2323.         /// If you're not using MainLoop, consider using Render3DEnvironment
  2324.         /// </summary>
  2325.         public void Render3DEnvironment()
  2326.         {
  2327.             Device device = State.Device;
  2328.             if (device == null)
  2329.                 return; // Nothing to do
  2330.  
  2331.             if (State.IsDeviceLost || State.IsRenderingPaused)
  2332.             {
  2333.                 // Window is minimized or paused so yield 
  2334.                 // CPU time to other processes
  2335.                 System.Threading.Thread.Sleep(100); 
  2336.             }
  2337.  
  2338.             if (!State.IsActive)
  2339.             {
  2340.                 // Window is not in focus so yield some CPU time to other processes
  2341.                 System.Threading.Thread.Sleep(20); 
  2342.             }
  2343.  
  2344.             if (State.IsDeviceLost && !State.IsRenderingPaused)
  2345.             {
  2346.                 int result;
  2347.                 // Check the cooperative level to see if it's ok to render
  2348.                 if (!device.CheckCooperativeLevel(out result))
  2349.                 {
  2350.                     if (result == (int)ResultCode.DeviceLost)
  2351.                     {
  2352.                         // The device has been lost but cannot be reset at this time.  
  2353.                         // So wait until it can be reset.
  2354.                         System.Threading.Thread.Sleep(50); 
  2355.                         return;
  2356.                     }
  2357.  
  2358.                     // If we are windowed, read the desktop format and 
  2359.                     // ensure that the Direct3D device is using the same format 
  2360.                     // since the user could have changed the desktop bitdepth 
  2361.                     if (IsWindowed)
  2362.                     {
  2363.                         DisplayMode adapterDesktopMode = Manager.Adapters[(int)State.CurrentDeviceSettings.AdapterOrdinal].CurrentDisplayMode;
  2364.                         if (State.CurrentDeviceSettings.AdapterFormat != adapterDesktopMode.Format)
  2365.                         {
  2366.                             // Set up the match options
  2367.                             MatchOptions match = new MatchOptions();
  2368.                             match.AdapterOrdinal = MatchType.PreserveInput;
  2369.                             match.DeviceType = MatchType.PreserveInput;
  2370.                             match.Windowed = MatchType.PreserveInput;
  2371.                             match.AdapterFormat = MatchType.PreserveInput;
  2372.                             match.VertexProcessing = MatchType.ClosestToInput;
  2373.                             match.Resolution = MatchType.ClosestToInput;
  2374.                             match.BackBufferFormat = MatchType.ClosestToInput;
  2375.                             match.BackBufferCount = MatchType.ClosestToInput;
  2376.                             match.MultiSample = MatchType.ClosestToInput;
  2377.                             match.SwapEffect = MatchType.ClosestToInput;
  2378.                             match.DepthFormat = MatchType.ClosestToInput;
  2379.                             match.StencilFormat = MatchType.ClosestToInput;
  2380.                             match.PresentFlags = MatchType.ClosestToInput;
  2381.                             match.RefreshRate = MatchType.ClosestToInput;
  2382.                             match.PresentInterval = MatchType.ClosestToInput;
  2383.  
  2384.                             DeviceSettings settings = State.CurrentDeviceSettings;
  2385.                             settings.AdapterFormat = adapterDesktopMode.Format;
  2386.                             try
  2387.                             {
  2388.                                 settings = FindValidDeviceSettings(settings, match);
  2389.                                 // Change to a Direct3D device created from the new device settings.  
  2390.                                 // If there is an existing device, then either reset or recreate the scene
  2391.                                 ChangeDevice(settings, null, false);
  2392.                             }
  2393.                             catch
  2394.                             {
  2395.                                 DisplayErrorMessage(new NoCompatibleDevicesException());
  2396.                                 Dispose();
  2397.                             }
  2398.                             return;
  2399.                         }
  2400.                     }
  2401.  
  2402.                     // Try to reset the device
  2403.                     try
  2404.                     {
  2405.                         Reset3DEnvironment();
  2406.                     }
  2407.                     catch (DeviceLostException)
  2408.                     {
  2409.                         // The device was lost again, so continue waiting until it can be reset.
  2410.                         System.Threading.Thread.Sleep(50); 
  2411.                         return;
  2412.                     }
  2413.                     catch
  2414.                     {
  2415.                         // Reset failed, but the device wasn't lost so something bad happened, 
  2416.                         // so recreate the device to try to recover
  2417.                         DeviceSettings deviceSettings = State.CurrentDeviceSettings;
  2418.                         try
  2419.                         {
  2420.                             ChangeDevice(deviceSettings, null, false);
  2421.                         }
  2422.                         catch (Exception e)
  2423.                         {
  2424.                             ResettingDeviceException rde = new ResettingDeviceException(e);
  2425.                             DisplayErrorMessage(rde);
  2426.                             Dispose();
  2427.                             throw;
  2428.                         }
  2429.                     }
  2430.                 }
  2431.                 State.IsDeviceLost = false;
  2432.             }
  2433.  
  2434.             // Get the app's time, in seconds. Skip rendering if no time elapsed
  2435.             double time = FrameworkTimer.GetTime();
  2436.             float elapsedTime = (float)FrameworkTimer.GetElapsedTime();
  2437.  
  2438.             // Store the time for the app
  2439.             if (State.IsUsingConstantFrameTime)
  2440.             {        
  2441.                 elapsedTime = State.TimePerFrame;
  2442.                 time = State.CurrentTime + elapsedTime;
  2443.             }
  2444.  
  2445.             State.CurrentTime = time;
  2446.             State.ElapsedTime = elapsedTime;
  2447.  
  2448.             // Update the FPS stats
  2449.             UpdateFrameStats();
  2450.  
  2451.             // If the settings dialog exists and is being shown, then 
  2452.             // render it instead of rendering the app's scene
  2453.             if (State.Settings != null && State.IsD3DSettingsDialogShowing)
  2454.             {
  2455.                 if (!State.IsRenderingPaused)
  2456.                 {
  2457.                     // Clear the render target and the zbuffer 
  2458.                     device.Clear(ClearFlags.Target, 0x00003F3F, 1.0f, 0);
  2459.  
  2460.                     // Render the scene
  2461.                     device.BeginScene();
  2462.                     State.Settings.OnRender(elapsedTime);
  2463.                     device.EndScene();
  2464.                 }
  2465.             }
  2466.             else
  2467.             {
  2468.                 HandleTimers();
  2469.  
  2470.                 //Animate the scene by calling the app's frame move callback
  2471.                 if (State.CallbackInterface != null)
  2472.                 {
  2473.                     State.CallbackInterface.OnFrameMove(device, time, elapsedTime);
  2474.                     device = State.Device;
  2475.                     if (device == null)
  2476.                         return;
  2477.                 }
  2478.  
  2479.                 if (!State.IsRenderingPaused)
  2480.                 {
  2481.                     // Render the scene by calling the app's render callback
  2482.                     if (State.CallbackInterface != null)
  2483.                     {
  2484.                         State.CallbackInterface.OnFrameRender(device, time, elapsedTime);
  2485.                         device = State.Device;
  2486.                         if (device == null)
  2487.                             return;
  2488.                     }
  2489.                 }
  2490.             }
  2491.  
  2492.             if (!State.IsRenderingPaused)
  2493.             {
  2494.                 // Show the frame on the primary surface
  2495.                 try
  2496.                 {
  2497.                     device.Present();
  2498.                 }
  2499.                 catch (DeviceLostException)
  2500.                 {
  2501.                     // Whoops, device is lost now
  2502.                     State.IsDeviceLost = true;
  2503.                 }
  2504.                 catch (DriverInternalErrorException)
  2505.                 {
  2506.                     // When DriverInternalErrorException is thrown from Present(),
  2507.                     // the application can do one of the following:
  2508.                     // 
  2509.                     // - End, with the pop-up window saying that the application cannot continue 
  2510.                     //   because of problems in the display adapter and that the user should 
  2511.                     //   contact the adapter manufacturer.
  2512.                     //
  2513.                     // - Attempt to restart by calling Device.Reset, which is essentially the same 
  2514.                     //   path as recovering from a lost device. If Device.Reset throws the 
  2515.                     //   DriverInternalErrorException, the application should end immediately with the 
  2516.                     //   message that the user should contact the adapter manufacturer.
  2517.                     // 
  2518.                     // The framework attempts the path of resetting the device
  2519.                     // 
  2520.                     State.IsDeviceLost = true;
  2521.                 }
  2522.             }
  2523.  
  2524.             // Update the current frame number
  2525.             State.CurrentFrameNumber++;
  2526.  
  2527.             if (State.OverrideQuitAfterFrame != 0)
  2528.             {
  2529.                 if (State.CurrentFrameNumber > State.OverrideQuitAfterFrame)
  2530.                     Dispose();
  2531.             }
  2532.         }
  2533.  
  2534.         /// <summary>
  2535.         /// Closes down the window.  When the window closes, it will cleanup everything
  2536.         /// </summary>
  2537.         private void Shutdown()
  2538.         {
  2539.             // Cleanup the environment
  2540.             Cleanup3DEnvironment(true);
  2541.             // Close the window
  2542.             if (Window != IntPtr.Zero)
  2543.             {
  2544.                 NativeMethods.SendMessage(Window, NativeMethods.WindowMessage.Close, IntPtr.Zero, IntPtr.Zero);
  2545.             }
  2546.         }
  2547.  
  2548.         /// <summary>
  2549.         /// Pauses time or rendering.  Keeps a ref count so pausing can be layered
  2550.         /// </summary>
  2551.         public void Pause(bool pauseTime, bool pauseRendering)
  2552.         {
  2553.             // Update counters
  2554.             State.PauseTimeCount += pauseTime ? +1 : -1;
  2555.             if (State.PauseTimeCount < 0)
  2556.                 State.PauseTimeCount = 0;
  2557.  
  2558.             State.PauseRenderingCount += pauseRendering ? +1 : -1;
  2559.             if (State.PauseRenderingCount < 0)
  2560.                 State.PauseRenderingCount = 0;
  2561.  
  2562.             if (State.PauseTimeCount > 0)
  2563.             {
  2564.                 // Stop the scene from animating
  2565.                 FrameworkTimer.Stop();
  2566.             }
  2567.             else
  2568.             {
  2569.                 // Restart the timer
  2570.                 FrameworkTimer.Start();
  2571.             }
  2572.  
  2573.             State.IsRenderingPaused = (State.PauseRenderingCount > 0);
  2574.             State.IsTimePaused = (State.PauseTimeCount > 0);
  2575.         }
  2576.  
  2577.  
  2578.         /// <summary>
  2579.         /// Display an custom error msg box
  2580.         /// </summary>
  2581.         public void DisplayErrorMessage(Exception e)
  2582.         {
  2583.             if (e == lastDisplayedMessage) 
  2584.                 return; //Already displayed this
  2585.  
  2586.             lastDisplayedMessage = e;
  2587.  
  2588.             State.ApplicationExitCode = 1;
  2589.             bool found = true;
  2590.             if (e is ApplicationException)
  2591.             {
  2592.                 if (e is NoDirect3DException)
  2593.                     State.ApplicationExitCode = 2;
  2594.                 else if (e is MediaNotFoundException)
  2595.                     State.ApplicationExitCode = 4;
  2596.                 else if (e is CreatingDeviceException)
  2597.                     State.ApplicationExitCode = 6;
  2598.                 else if (e is ResettingDeviceException)
  2599.                     State.ApplicationExitCode = 7;
  2600.                 else if (e is CreatingDeviceObjectsException)
  2601.                     State.ApplicationExitCode = 8;
  2602.                 else if (e is ResettingDeviceObjectsException)
  2603.                     State.ApplicationExitCode = 9;
  2604.                 else if (e is NoCompatibleDevicesException)
  2605.                     State.ApplicationExitCode = 3;
  2606.                 else
  2607.                     found = false;
  2608.             }
  2609.             else
  2610.                 found = false;
  2611.  
  2612.             string errorText = string.Empty;
  2613.             if (found && State.IsShowingMsgBoxOnError)
  2614.             {
  2615.                 errorText = e.Message;
  2616.                 if (e.InnerException != null)
  2617.                 {
  2618.                     errorText += "\r\n\r\n" + e.InnerException.ToString();
  2619.                     lastDisplayedMessage = e.InnerException;
  2620.                 }
  2621.  
  2622.                 if (State.WindowTitle != null && State.WindowTitle.Length > 0)
  2623.                     System.Windows.Forms.MessageBox.Show(errorText, "DirectX Application", 
  2624.                         System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
  2625.                 else
  2626.                     System.Windows.Forms.MessageBox.Show(errorText, State.WindowTitle, 
  2627.                         System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
  2628.             }
  2629.             else if (!found && State.IsShowingMsgBoxOnError)
  2630.             {
  2631.                 errorText = e.ToString();
  2632.                 // Unknown error, but they still want to see them
  2633.                 if (State.WindowTitle != null && State.WindowTitle.Length > 0)
  2634.                     System.Windows.Forms.MessageBox.Show(errorText, "DirectX Application", 
  2635.                         System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
  2636.                 else
  2637.                     System.Windows.Forms.MessageBox.Show(errorText, State.WindowTitle, 
  2638.                         System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
  2639.             }
  2640.         }
  2641.  
  2642.         /// <summary>
  2643.         ///  Cleans up the 3D environment by:
  2644.         ///  - Calls the device lost callback 
  2645.         ///  - Calls the device destroyed callback 
  2646.         ///  - Disposes the D3D device
  2647.         /// </summary>
  2648.         private void Cleanup3DEnvironment(bool releaseSettings)
  2649.         {
  2650.             DialogResourceManager.GetGlobalInstance().OnLostDevice();
  2651.             DialogResourceManager.GetGlobalInstance().OnDestroyDevice();
  2652.             ResourceCache.GetGlobalInstance().OnLostDevice();
  2653.             ResourceCache.GetGlobalInstance().OnDestroyDevice();
  2654.  
  2655.             using(Device device = State.Device)
  2656.             {
  2657.                 if (releaseSettings)
  2658.                 {
  2659.                     // Get rid of settings
  2660.                     State.CurrentDeviceSettings = null;
  2661.                 }
  2662.  
  2663.                 // Empty out other structures
  2664.                 State.BackBufferSurfaceDesc = new SurfaceDescription();
  2665.                 State.Caps = new Caps();
  2666.                 State.WasDeviceCreated = false;
  2667.             }
  2668.             System.Diagnostics.Debug.Assert(State.Device.Disposed, "Device should be disposed here.");
  2669.             // No more device
  2670.             State.Device = null;
  2671.         }
  2672.  
  2673.         /// <summary>
  2674.         /// Initializes the 3D environment by:
  2675.         /// - Adjusts the window size, style, and menu 
  2676.         /// - Stores the back buffer description
  2677.         /// - Sets up the full screen Direct3D cursor if requested
  2678.         /// - Calls the device created callback
  2679.         /// - Calls the device reset callback
  2680.         /// - If both callbacks succeed it unpauses the app 
  2681.         /// </summary>
  2682.         private void Initialize3DEnvironment()
  2683.         {
  2684.             Device device = State.Device;
  2685.             System.Diagnostics.Debug.Assert(device != null, "The device should not be null here.");
  2686.             if (device == null)
  2687.                 throw new InvalidOperationException("The device should not be null.");
  2688.  
  2689.             bool success = true;
  2690.             try
  2691.             {
  2692.                 State.AreDeviceObjectsCreated = false;
  2693.                 State.AreDeviceObjectsReset = false;
  2694.  
  2695.                 // Prepare the window for a possible change between windowed mode 
  2696.                 // and full screen mode by adjusting the window style and its menu.
  2697.                 AdjustWindowStyle(Window, IsWindowed);
  2698.  
  2699.                 // Prepare the device with cursor info and 
  2700.                 // store backbuffer desc and caps from the device
  2701.                 PrepareDevice(device);
  2702.  
  2703.                 try
  2704.                 {
  2705.                     // If the settings dialog exists call its OnCreateDevice() and OnResetDevice()
  2706.                     if (State.Settings != null)
  2707.                     {
  2708.                         State.Settings.OnCreateDevice(device);
  2709.                         State.Settings.OnResetDevice();
  2710.                     }
  2711.                     // Call the dialog resource manager create device method
  2712.                     DialogResourceManager.GetGlobalInstance().OnCreateDevice(device);
  2713.  
  2714.                     // Call the resource cache created function
  2715.                     ResourceCache.GetGlobalInstance().OnCreateDevice(device);
  2716.  
  2717.                     // Call the applications device created callback if it's set
  2718.                     State.IsInsideDeviceCallback = true;
  2719.                     if (DeviceCreated != null)
  2720.                     {
  2721.                         DeviceCreated(this, new DeviceEventArgs(device, State.BackBufferSurfaceDesc));
  2722.                     }
  2723.                     State.IsInsideDeviceCallback = false;
  2724.                     State.AreDeviceObjectsCreated = true;
  2725.                 }
  2726.                 catch (MediaNotFoundException e)
  2727.                 {
  2728.                     DisplayErrorMessage(e);
  2729.                     success = false;
  2730.                     throw;
  2731.                 }
  2732.                 catch (Exception e)
  2733.                 {
  2734.                     DisplayErrorMessage(new CreatingDeviceException(e));
  2735.                     success = false;
  2736.                     throw;
  2737.                 }
  2738.  
  2739.                 try
  2740.                 {
  2741.                     // Call the dialog resource manager reset device method
  2742.                     DialogResourceManager.GetGlobalInstance().OnResetDevice(device);
  2743.  
  2744.                     // Call the resource cache device reset function
  2745.                     ResourceCache.GetGlobalInstance().OnResetDevice(device);
  2746.  
  2747.                     // Call app's reset
  2748.                     State.IsInsideDeviceCallback = true;
  2749.                     if (DeviceReset != null)
  2750.                         DeviceReset(this, new DeviceEventArgs(device, State.BackBufferSurfaceDesc));
  2751.  
  2752.                     State.IsInsideDeviceCallback = false;
  2753.                     State.AreDeviceObjectsReset = true;
  2754.                 }
  2755.                 catch (MediaNotFoundException e)
  2756.                 {
  2757.                     DisplayErrorMessage(e);
  2758.                     success = false;
  2759.                     throw;
  2760.                 }
  2761.                 catch (Exception e)
  2762.                 {
  2763.                     DisplayErrorMessage(new CreatingDeviceException(e));
  2764.                     success = false;
  2765.                     throw;
  2766.                 }
  2767.             }
  2768.             finally
  2769.             {
  2770.                 if (!success)
  2771.                     Cleanup3DEnvironment(true);
  2772.             }
  2773.  
  2774.         }
  2775.  
  2776.         /// <summary>
  2777.         /// Prepares a new or resetting device by with cursor info and 
  2778.         /// store backbuffer desc and caps from the device
  2779.         /// </summary>
  2780.         private void PrepareDevice(Device device)
  2781.         {
  2782.             // Update the device stats text
  2783.             UpdateStaticFrameStats();
  2784.  
  2785.             // Store render target surface desc
  2786.             using(Surface backBuffer = device.GetBackBuffer(0, 0, BackBufferType.Mono))
  2787.             {
  2788.                 State.BackBufferSurfaceDesc = backBuffer.Description;
  2789.             }
  2790.  
  2791.             // Update the state's copy of caps
  2792.             State.Caps = device.DeviceCaps;
  2793.  
  2794.             // Setup the full screen cursor
  2795.             if (State.IsShowingCursorWhenFullScreen && !IsWindowed)
  2796.             {
  2797.                 // Get a valid cursor
  2798.                 System.Windows.Forms.Cursor cursor = System.Windows.Forms.Cursor.Current;
  2799.                 if (cursor == null)
  2800.                     cursor = System.Windows.Forms.Cursors.Default;
  2801.  
  2802.                 device.SetCursor(cursor, false);
  2803.                 device.ShowCursor(true);
  2804.             }
  2805.  
  2806.             // Confine cursor to full screen window
  2807.             if (State.IsCursorClippedWhenFullScreen)
  2808.             {
  2809.                 if (!IsWindowed)
  2810.                 {
  2811.                     // Turn on clipping for the full screen window
  2812.                     System.Drawing.Rectangle windowRect;
  2813.                     NativeMethods.GetWindowRect(Window, out windowRect);
  2814.                     System.Windows.Forms.Cursor.Clip = windowRect;
  2815.                 }
  2816.                 else
  2817.                 {
  2818.                     // Turn off clipping
  2819.                     System.Windows.Forms.Cursor.Clip = System.Drawing.Rectangle.Empty;
  2820.                 }
  2821.             }
  2822.         }
  2823.  
  2824.         /// <summary>
  2825.         /// Prepare the window for a possible change between windowed mode and full screen mode
  2826.         /// </summary>
  2827.         private void AdjustWindowStyle(IntPtr window, bool isWindowed)
  2828.         {
  2829.             if (window == IntPtr.Zero) // nothing to do
  2830.                 return;
  2831.  
  2832.             if (isWindowed)
  2833.             {
  2834.                 // If different device windows are used for windowed mode and fullscreen mode,
  2835.                 // hide the fullscreen window so that it doesn't obscure the screen.
  2836.                 if (State.WindowDeviceWindowed != State.WindowDeviceFullScreen)
  2837.                 {
  2838.                     NativeMethods.ShowWindow(State.WindowDeviceFullScreen,
  2839.                         NativeMethods.ShowWindowFlags.Hide);
  2840.                 }
  2841.  
  2842.                 // Set the window style mode
  2843.                 NativeMethods.SetStyle(window, State.WindowStyle);
  2844.                 if (State.Menu != null)
  2845.                 {
  2846.                     NativeMethods.SetMenu(window, State.Menu.Handle);
  2847.                 }
  2848.             }
  2849.             else
  2850.             {
  2851.                 // If different device windows are used for windowed mode and fullscreen mode,
  2852.                 // restore and show the fullscreen device window.
  2853.                 if (State.WindowDeviceWindowed != State.WindowDeviceFullScreen)
  2854.                 {
  2855.                     if (NativeMethods.IsIconic(WindowDeviceFullscreen))
  2856.                         NativeMethods.ShowWindow(WindowDeviceFullscreen, NativeMethods.ShowWindowFlags.Restore);
  2857.  
  2858.                     NativeMethods.ShowWindow(State.WindowDeviceFullScreen,
  2859.                         NativeMethods.ShowWindowFlags.Show);
  2860.                 }
  2861.                 // Set the full screen style mode
  2862.                 NativeMethods.SetStyle(window, NativeMethods.WindowStyles.Popup | NativeMethods.WindowStyles.SystemMenu |
  2863.                     NativeMethods.WindowStyles.Visible);
  2864.             }
  2865.         }
  2866.  
  2867.         /// <summary>
  2868.         /// Updates the frames/sec stat once per second
  2869.         /// </summary>
  2870.         private void UpdateFrameStats()
  2871.         {
  2872.             // Keep track of frame count
  2873.             double time = FrameworkTimer.GetAbsoluteTime();
  2874.             State.LastStatsUpdateFrames++;
  2875.  
  2876.             if (time - State.LastStatsUpdateTime > 1.0)
  2877.             {
  2878.                 float fps = (float)(State.LastStatsUpdateFrames / (time - State.LastStatsUpdateTime));
  2879.                 State.CurrentFrameRate = fps;
  2880.                 State.LastStatsUpdateFrames = 0;
  2881.                 State.LastStatsUpdateTime = time;
  2882.  
  2883.                 State.FrameStats = string.Format(State.StaticFrameStats, fps.ToString("f2",
  2884.                     System.Globalization.CultureInfo.CurrentUICulture));
  2885.             }
  2886.         }
  2887.  
  2888.         /// <summary>
  2889.         /// Updates the static part of the frame stats so it doesn't have be generated every frame
  2890.         /// </summary>
  2891.         private void UpdateStaticFrameStats()
  2892.         {
  2893.             DeviceSettings settings = State.CurrentDeviceSettings;
  2894.             if (settings.presentParams == null)
  2895.                 return; // Nothing to do
  2896.  
  2897.             EnumDeviceSettingsCombo combo = Enumeration.GetDeviceSettingsCombo(
  2898.                 settings.AdapterOrdinal, settings.DeviceType, settings.AdapterFormat,
  2899.                 settings.presentParams.BackBufferFormat, settings.presentParams.Windowed);
  2900.  
  2901.             if (combo == null)
  2902.                 return; // Still nothing to do 
  2903.  
  2904.             // Get adapter/backbuffer format string
  2905.             string formatString = null;
  2906.             if (settings.AdapterFormat == combo.BackBufferFormat)
  2907.             {
  2908.                 // Get the format string
  2909.                 formatString = "Format." + settings.AdapterFormat.ToString();
  2910.             }
  2911.             else
  2912.             {
  2913.                 formatString = string.Format("backbuffer Format.{0}, adapter Format.{1}",
  2914.                     combo.BackBufferFormat, settings.AdapterFormat);
  2915.             }
  2916.  
  2917.             // Get depth stencil format string
  2918.             string depthFormat = null;
  2919.             if (settings.presentParams.EnableAutoDepthStencil)
  2920.             {
  2921.                 depthFormat = " Format." + settings.presentParams.AutoDepthStencilFormat.ToString();
  2922.             }
  2923.  
  2924.             // Get any multisampling string
  2925.             string multiSampleString = null;
  2926.             switch(settings.presentParams.MultiSample)
  2927.             {
  2928.                 case MultiSampleType.None:
  2929.                     multiSampleString = null; // No display
  2930.                     break;
  2931.                 case MultiSampleType.NonMaskable:
  2932.                     multiSampleString = " (Nonmaskable Multisample)";
  2933.                     break;
  2934.                 default:
  2935.                     multiSampleString = string.Format(" ({0} Multisample)", settings.presentParams.MultiSample.ToString());
  2936.                     break;
  2937.             }
  2938.  
  2939.             // Update static frame stats now
  2940.             State.StaticFrameStats = string.Format("{0} fps ({1}x{2}), {3}{4}{5}",
  2941.                 "{0}", settings.presentParams.BackBufferWidth, settings.presentParams.BackBufferHeight,
  2942.                 formatString, depthFormat, multiSampleString);
  2943.         }
  2944.  
  2945.         /// <summary>
  2946.         /// public helper function to return the adapter format from the first device settings 
  2947.         /// combo that matches the passed adapter ordinal, device type, backbuffer format, and windowed.  
  2948.         /// </summary>
  2949.         private Format FindAdapterFormat(uint adapterOrdinal, DeviceType deviceType, Format backbufferFormat,
  2950.             bool isWindowed)
  2951.         {
  2952.             EnumDeviceInformation deviceInfo = Enumeration.GetDeviceInfo(adapterOrdinal, deviceType);
  2953.             if (deviceInfo != null)
  2954.             {
  2955.                 for (int i = 0; i < deviceInfo.deviceSettingsList.Count; i++)
  2956.                 {
  2957.                     EnumDeviceSettingsCombo combo = deviceInfo.deviceSettingsList[i] as EnumDeviceSettingsCombo;
  2958.                     if ( (combo.BackBufferFormat == backbufferFormat) &&
  2959.                         (combo.IsWindowed == isWindowed) )
  2960.                     {
  2961.                         // return first match
  2962.                         return combo.AdapterFormat;
  2963.                     }
  2964.                 }
  2965.             }
  2966.  
  2967.             // Couldn't find any adapter format's.. Return back buffer format
  2968.             return backbufferFormat;
  2969.         }
  2970.  
  2971.  
  2972.         /// <summary>
  2973.         /// Checks if the window client rect has changed and if it has, then reset the device
  2974.         /// </summary>
  2975.         private void HandlePossibleSizeChange()
  2976.         {
  2977.             if ( (!State.WasDeviceCreated) || (State.AreSizeChangesIgnored) )
  2978.                 return; // Nothing to do here
  2979.  
  2980.             if (!State.CurrentDeviceSettings.presentParams.Windowed)
  2981.                 return; // nothing to do here either, can only resize in windowed
  2982.  
  2983.             System.Drawing.Rectangle oldClient = State.ClientRectangle;
  2984.             System.Drawing.Rectangle newClient;
  2985.             NativeMethods.GetClientRect(Window, out newClient);
  2986.             // Now store the new client
  2987.             State.ClientRectangle = newClient;
  2988.  
  2989.             // Don't forget the bounds rect
  2990.             System.Drawing.Rectangle windowBounds;
  2991.             NativeMethods.GetWindowRect(Window, out windowBounds);
  2992.             // Now store the new bounds
  2993.             State.WindowBoundsRectangle = windowBounds;
  2994.  
  2995.             // Check if the old client rect has changed
  2996.             if (oldClient.Width != newClient.Width || oldClient.Height != newClient.Height)
  2997.             {
  2998.                 // A new window size will require a new backbuffer
  2999.                 // size, so the 3D structures must be changed accordingly.
  3000.                 Pause(true, true);
  3001.  
  3002.                 DeviceSettings settings = State.CurrentDeviceSettings;
  3003.                 settings.presentParams.BackBufferWidth = newClient.Width;
  3004.                 settings.presentParams.BackBufferHeight = newClient.Height;
  3005.  
  3006.                 // Reset the 3D environment
  3007.                 if (State.Device != null)
  3008.                 {
  3009.                     try
  3010.                     {
  3011.                         Reset3DEnvironment();
  3012.                     }
  3013.                     catch
  3014.                     {
  3015.                         State.IsDeviceLost = true;
  3016.                     }
  3017.                 }
  3018.  
  3019.                 Pause(false, false);
  3020.             }
  3021.  
  3022.             CheckForWindowMonitorChange();
  3023.         }
  3024.  
  3025.         /// <summary>
  3026.         /// Checks to see if the window changed monitors, and if it did it creates a device 
  3027.         /// from the monitor's adapter and recreates the scene.
  3028.         /// </summary>
  3029.         private void CheckForWindowMonitorChange()
  3030.         {
  3031.             // Don't do this if the user doesn't want it
  3032.             if (!State.CanAutoChangeAdapter)
  3033.                 return; 
  3034.  
  3035.             IntPtr monitorHandle = NativeMethods.MonitorFromWindow(Window, 1); // Primary monitor
  3036.             if (monitorHandle != State.AdapterMonitor)
  3037.             {
  3038.                 // Changing device, pause
  3039.                 Pause(true, true);
  3040.                 // Get the new ordinal
  3041.                 uint newOrdinal = GetAdapterOridinalFromMonitor(monitorHandle);
  3042.                 
  3043.                 // Get the current device settings and flip the ordinal then
  3044.                 // find the closest valid device settings with this change
  3045.                 DeviceSettings settings = State.CurrentDeviceSettings.Clone();
  3046.                 settings.AdapterOrdinal = newOrdinal;
  3047.  
  3048.                 // Set up the match options
  3049.                 MatchOptions match = new MatchOptions();
  3050.                 match.AdapterOrdinal = MatchType.PreserveInput;
  3051.                 match.DeviceType = MatchType.ClosestToInput;
  3052.                 match.Windowed = MatchType.ClosestToInput;
  3053.                 match.AdapterFormat = MatchType.ClosestToInput;
  3054.                 match.VertexProcessing = MatchType.ClosestToInput;
  3055.                 match.Resolution = MatchType.ClosestToInput;
  3056.                 match.BackBufferFormat = MatchType.ClosestToInput;
  3057.                 match.BackBufferCount = MatchType.ClosestToInput;
  3058.                 match.MultiSample = MatchType.ClosestToInput;
  3059.                 match.SwapEffect = MatchType.ClosestToInput;
  3060.                 match.DepthFormat = MatchType.ClosestToInput;
  3061.                 match.StencilFormat = MatchType.ClosestToInput;
  3062.                 match.PresentFlags = MatchType.ClosestToInput;
  3063.                 match.RefreshRate = MatchType.ClosestToInput;
  3064.                 match.PresentInterval = MatchType.ClosestToInput;
  3065.  
  3066.                 try
  3067.                 {
  3068.                     // Find some valid settings (if possible)
  3069.                     settings = FindValidDeviceSettings(settings, match);
  3070.                     try
  3071.                     {
  3072.                         // Create a Direct3D device using the new device settings.  
  3073.                         // If there is an existing device, then it will either reset or recreate the scene.
  3074.                         ChangeDevice(settings, null, false);
  3075.                     }
  3076.                     catch (Exception e)
  3077.                     {
  3078.                         // This is a much worse error.. Shut down and fail
  3079.                         Dispose();
  3080.                         Pause(false, false);
  3081.                         System.Diagnostics.Debugger.Log(0, string.Empty, e.ToString());
  3082.                         return;
  3083.                     }
  3084.                 } 
  3085.                 catch
  3086.                 {
  3087.                     // No valid device was found for this monitor, that's bad, but not fatal.
  3088.                     // Ignore this error
  3089.                 } 
  3090.  
  3091.             }
  3092.             Pause(false, false);
  3093.         }
  3094.  
  3095.         /// <summary>Look for an adapter ordinal that is tied to a monitor handle</summary>
  3096.         private uint GetAdapterOridinalFromMonitor(IntPtr monitorHandle)
  3097.         {
  3098.             uint ordinal = 0;
  3099.  
  3100.             foreach(EnumAdapterInformation info in Enumeration.AdapterInformationList)
  3101.             {
  3102.                 if (Manager.GetAdapterMonitor((int)info.AdapterOrdinal) == monitorHandle)
  3103.                     ordinal = info.AdapterOrdinal;
  3104.             }
  3105.             return ordinal;
  3106.         }
  3107.  
  3108.         /// <summary>Will try to load the first icon from the resources</summary>
  3109.         public System.Drawing.Icon LoadFirstIconFromResource()
  3110.         {
  3111.             // Get the assembly that is calling this method
  3112.             System.Reflection.Assembly currentAssembly = System.Reflection.Assembly.GetCallingAssembly();
  3113.             foreach(string s in currentAssembly.GetManifestResourceNames())
  3114.             {
  3115.                 try
  3116.                 {
  3117.                     // See if there are any .icos
  3118.                     System.IO.Stream resourceStream = currentAssembly.GetManifestResourceStream(s);
  3119.                     return new System.Drawing.Icon(resourceStream);
  3120.                 }
  3121.                 catch 
  3122.                 {
  3123.                     // No icon yet, try reading the resources in a different way
  3124.                     try
  3125.                     {
  3126.                         System.Resources.ResourceReader reader = new System.Resources.ResourceReader(currentAssembly.GetManifestResourceStream(s));
  3127.                         IDictionaryEnumerator en = reader.GetEnumerator();
  3128.                         while(en.MoveNext())
  3129.                         {
  3130.                             System.Drawing.Icon test = en.Value as System.Drawing.Icon;
  3131.                             if (test != null)
  3132.                                 return test;
  3133.                         }
  3134.                     }
  3135.                     catch 
  3136.                     {
  3137.                         // Didn't find an icon yet, continue
  3138.                         continue;
  3139.                     }
  3140.                 }   
  3141.             }
  3142.             // It's no big deal if we can't load the icon, just return null instead
  3143.             return null;
  3144.         }
  3145.  
  3146.         /// <summary>Handles setting up the cursor settings for fullscreen</summary>
  3147.         public void SetCursorSettings(bool showCursorWhenFullscreen, bool clipCursorWhenFullscreen)
  3148.         {
  3149.             State.IsCursorClippedWhenFullScreen = clipCursorWhenFullscreen;
  3150.             State.IsShowingCursorWhenFullScreen = showCursorWhenFullscreen;
  3151.         }
  3152.  
  3153.         /// <summary>Determines if the adapter can automatically change monitors</summary>
  3154.         public bool CanAutomaticallyChangeMonitor
  3155.         {
  3156.             get { return State.CanAutoChangeAdapter; } set { State.CanAutoChangeAdapter = value; }
  3157.         }
  3158.  
  3159.         /// <summary>Allows to set constant time per frame</summary>
  3160.         public void SetConstantFrameTime(bool constantFrameTime, float timePerFrame)
  3161.         {
  3162.             State.OverrideConstantTimePerFrame = timePerFrame;
  3163.             State.IsUsingConstantFrameTime = constantFrameTime;
  3164.         }
  3165.         /// <summary>Allows to set constant time per frame</summary>
  3166.         public void SetConstantFrameTime(bool constantFrameTime)
  3167.         {
  3168.             State.OverrideConstantTimePerFrame = 0.0333f;
  3169.             State.IsUsingConstantFrameTime = constantFrameTime;
  3170.         }
  3171.  
  3172.         /// <summary>
  3173.         /// Exit code from the framework
  3174.         /// </summary>
  3175.         public int ExitCode
  3176.         {
  3177.             get { return State.ApplicationExitCode; }
  3178.         }
  3179.  
  3180.         /// <summary>
  3181.         /// Show the settings dialog, and create if needed
  3182.         /// </summary>
  3183.         public void ShowSettingsDialog(bool shouldShow)
  3184.         {
  3185.             State.IsD3DSettingsDialogShowing = shouldShow;
  3186.  
  3187.             if (shouldShow)
  3188.             {
  3189.                 SettingsDialog dialog = PrepareSettingsDialog();
  3190.                 dialog.Refresh();
  3191.             }
  3192.         }
  3193.  
  3194.         /// <summary>
  3195.         /// public helper function to prepare the settings dialog by creating it if it didn't 
  3196.         /// already exist and enumerating if desired.
  3197.         /// </summary>
  3198.         /// <returns></returns>
  3199.         private SettingsDialog PrepareSettingsDialog()
  3200.         {
  3201.             SettingsDialog dialog = State.Settings;
  3202.             if (dialog == null)
  3203.             {
  3204.                 // Create it
  3205.                 dialog = new SettingsDialog(this);
  3206.                 State.Settings = dialog; // Store it too
  3207.  
  3208.                 if (State.AreDeviceObjectsCreated)
  3209.                 {
  3210.                     dialog.OnCreateDevice(State.Device);
  3211.                 }
  3212.                 if (State.AreDeviceObjectsReset)
  3213.                 {
  3214.                     dialog.OnResetDevice();
  3215.                 }
  3216.             }
  3217.  
  3218.             return dialog;
  3219.         }
  3220.  
  3221.         #region External state access functions
  3222.         /// <summary>
  3223.         /// Returns the device current in use for the framework
  3224.         /// </summary>
  3225.         public Device Device
  3226.         {
  3227.             get { return State.Device;}
  3228.         }
  3229.  
  3230.         /// <summary>
  3231.         /// Returns the back buffer surface description
  3232.         /// </summary>
  3233.         public SurfaceDescription BackBufferSurfaceDescription
  3234.         {
  3235.             get { return State.BackBufferSurfaceDesc; }
  3236.         }
  3237.  
  3238.         /// <summary>
  3239.         /// Returns the device capabilities
  3240.         /// </summary>
  3241.         public Caps DeviceCaps
  3242.         {
  3243.             get { return State.Caps; }
  3244.         }
  3245.  
  3246.         /// <summary>
  3247.         /// Get the current window used for rendering
  3248.         /// </summary>
  3249.         public IntPtr Window
  3250.         {
  3251.             get { return IsWindowed ? State.WindowDeviceWindowed : State.WindowDeviceFullScreen; }
  3252.         }
  3253.  
  3254.         /// <summary>
  3255.         /// Get the current focus window 
  3256.         /// </summary>
  3257.         public IntPtr WindowFocus
  3258.         {
  3259.             get { return State.WindowFocus; }
  3260.         }
  3261.  
  3262.         /// <summary>
  3263.         /// Get the current window used for windowed rendering
  3264.         /// </summary>
  3265.         public IntPtr WindowDeviceWindowed
  3266.         {
  3267.             get { return State.WindowDeviceWindowed; }
  3268.         }
  3269.  
  3270.         /// <summary>
  3271.         /// Get the current window used for full screen rendering
  3272.         /// </summary>
  3273.         public IntPtr WindowDeviceFullscreen
  3274.         {
  3275.             get { return State.WindowDeviceFullScreen; }
  3276.         }
  3277.  
  3278.         /// <summary>
  3279.         /// Client rectangle size
  3280.         /// </summary>
  3281.         public System.Drawing.Rectangle WindowClientRectangle
  3282.         {
  3283.             get { return State.ClientRectangle; }
  3284.         }
  3285.  
  3286.         /// <summary>
  3287.         /// Client rectangle size
  3288.         /// </summary>
  3289.         public System.Drawing.Rectangle ClientRectangle
  3290.         {
  3291.             get { return IsWindowed ? State.ClientRectangle : State.FullScreenClientRectangle; }
  3292.         }
  3293.         
  3294.         /// <summary>
  3295.         /// Return if windowed in the current device.  If no device exists yet, return false
  3296.         /// </summary>
  3297.         public bool IsWindowed
  3298.         {
  3299.             get 
  3300.             {
  3301.                 DeviceSettings settings = State.CurrentDeviceSettings;
  3302.                 if ((settings != null) && (settings.presentParams != null))
  3303.                     return settings.presentParams.Windowed;
  3304.                 else
  3305.                     return false;
  3306.             }
  3307.         }
  3308.  
  3309.  
  3310.         /// <summary>
  3311.         /// Frame statistics
  3312.         /// </summary>
  3313.         public string FrameStats
  3314.         {
  3315.             get { return State.FrameStats; }
  3316.         }
  3317.         /// <summary>
  3318.         /// Device statistics
  3319.         /// </summary>
  3320.         public string DeviceStats
  3321.         {
  3322.             get { return State.DeviceStats; }
  3323.         }
  3324.  
  3325.         /// <summary>
  3326.         /// Frames per second application is running at
  3327.         /// </summary>
  3328.         public float FPS
  3329.         {
  3330.             get { return State.CurrentFrameRate; }
  3331.         }
  3332.  
  3333.         /// <summary>
  3334.         /// Returns whether the settings is showing
  3335.         /// </summary>
  3336.         public bool IsD3DSettingsDialogShowing
  3337.         {
  3338.             get { return State.IsD3DSettingsDialogShowing; }
  3339.         }
  3340.  
  3341.         /// <summary>
  3342.         /// Return the device settings of the current device.  If no device exists yet, then
  3343.         /// return blank device settings 
  3344.         /// </summary>
  3345.         public DeviceSettings DeviceSettings
  3346.         {
  3347.             get 
  3348.             { 
  3349.                 if (State.CurrentDeviceSettings != null)
  3350.                 {
  3351.                     return State.CurrentDeviceSettings;
  3352.                 }
  3353.                 else
  3354.                 {
  3355.                     return new DeviceSettings();
  3356.                 }
  3357.             }
  3358.         }
  3359.         /// <summary>
  3360.         /// Return the present params of the current device.  If no device exists yet, then
  3361.         /// return blank present params 
  3362.         /// </summary>
  3363.         public PresentParameters PresentParameters
  3364.         {
  3365.             get 
  3366.             { 
  3367.                 if (State.CurrentDeviceSettings != null)
  3368.                 {
  3369.                     return State.CurrentDeviceSettings.presentParams;
  3370.                 }
  3371.                 else
  3372.                 {
  3373.                     return new PresentParameters();
  3374.                 }
  3375.             }
  3376.         }
  3377.         /// <summary>Should the framework also notify for mouse moves</summary>
  3378.         public bool IsNotifiedOnMouseMove
  3379.         {
  3380.             get { return State.IsNotifiedOnMouseMove; } set { State.IsNotifiedOnMouseMove = value;}
  3381.         }
  3382.  
  3383.         /// <summary>Should the framework override and start fullscreen?</summary>
  3384.         public bool IsOverridingFullScreen 
  3385.         {
  3386.             get { return State.IsOverridingFullScreen; } set { State.IsOverridingFullScreen = value;}
  3387.         }
  3388.  
  3389.         /// <summary>Should the framework ignore size changes?</summary>
  3390.         public bool IsIgnoringSizeChanges { get { return State.AreSizeChangesIgnored; } set { State.AreSizeChangesIgnored = value; } }
  3391.  
  3392.         /// <summary>Resets the state associated with the sample framework</summary>
  3393.         public void ResetState()
  3394.         {
  3395.             State = new FrameworkData();
  3396.         }
  3397.         #endregion
  3398.  
  3399.         #region Timer Callbacks
  3400.         /// <summary>Adds a timer to the list of timers for the application</summary>
  3401.         /// <returns>A unqiue id of the timer</returns>
  3402.         public int SetTimer(TimerCallback callbackTimer, float timeoutInSeconds)
  3403.         {
  3404.             if (callbackTimer == null)
  3405.                 throw new ArgumentNullException("callbackTimer", "You must pass in a valid timer callback .");
  3406.  
  3407.             TimerData timer = new TimerData();
  3408.             timer.callback = callbackTimer;
  3409.             timer.Countdown = timeoutInSeconds;
  3410.             timer.TimeoutInSecs = timeoutInSeconds;
  3411.             timer.IsEnabled = true;
  3412.  
  3413.             State.Timers.Add(timer);
  3414.             return State.Timers.Count;
  3415.         }
  3416.  
  3417.         /// <summary>Kills an already created timer</summary>
  3418.         public void KillTimer(int id)
  3419.         {
  3420.             if (id < State.Timers.Count)
  3421.             {
  3422.                 TimerData timer = (TimerData)State.Timers[id];
  3423.                 timer.IsEnabled = false;
  3424.                 State.Timers[id] = timer;
  3425.             }
  3426.             else
  3427.             {
  3428.                 throw new ArgumentException("An invalid timer ID was passed in", "id");
  3429.             }
  3430.         }
  3431.  
  3432.         /// <summary>public helper function to handle calling the user defined timer callbacks</summary>
  3433.         private void HandleTimers()
  3434.         {
  3435.             float elapsedTime = State.ElapsedTime;
  3436.  
  3437.             // Walk through the list of timers
  3438.             for (int i = 0; i < State.Timers.Count; i++)
  3439.             {
  3440.                 TimerData ft = (TimerData)State.Timers[i];
  3441.                 if (ft.IsEnabled)
  3442.                 {
  3443.                     ft.Countdown -= elapsedTime;
  3444.                     // Call the callback if the countdown expired
  3445.                     if (ft.Countdown < 0)
  3446.                     {
  3447.                         ft.callback((uint)i);
  3448.                         ft.Countdown = ft.TimeoutInSecs;
  3449.                     }
  3450.                     State.Timers[i] = ft;
  3451.                 }
  3452.             }
  3453.         }
  3454.         #endregion
  3455.  
  3456.         /// <summary>Will close the window</summary>
  3457.         public void CloseWindow()
  3458.         {
  3459.             if (Window != IntPtr.Zero)
  3460.                 NativeMethods.SendMessage(Window, NativeMethods.WindowMessage.Close, IntPtr.Zero, IntPtr.Zero);
  3461.         }
  3462.  
  3463.         #region Window Message Handling
  3464.         /// <summary>Handles window messages</summary>
  3465.         private IntPtr WindowsProcedure(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam)
  3466.         {
  3467.             if (State.Settings != null && State.IsD3DSettingsDialogShowing)
  3468.             {
  3469.                 State.Settings.HandleMessages(hWnd, msg, wParam, lParam);
  3470.             }
  3471.             else
  3472.             {
  3473.                 // Consolidate the keyboard messages and pass them to the app's keyboard callback
  3474.                 if (State.KeyboardFunction != null)
  3475.                 {
  3476.                     if( msg == NativeMethods.WindowMessage.KeyDown ||
  3477.                         msg == NativeMethods.WindowMessage.SystemKeyDown || 
  3478.                         msg == NativeMethods.WindowMessage.KeyUp ||
  3479.                         msg == NativeMethods.WindowMessage.SystemKeyUp )
  3480.                     {
  3481.                         bool isKeyDown = (msg == NativeMethods.WindowMessage.KeyDown
  3482.                             || msg == NativeMethods.WindowMessage.SystemKeyDown);
  3483.                         
  3484.                         uint mask = (1 << 29);
  3485.                         bool isAltDown = ( (lParam.ToInt32() & mask) != 0 );
  3486.                         State.KeyboardFunction((System.Windows.Forms.Keys)wParam.ToInt32(), isKeyDown, isAltDown );           
  3487.                     }
  3488.                 }
  3489.                 // Consolidate the mouse button messages and pass them to the app's mouse callback
  3490.                 if (State.MouseFunction != null)
  3491.                 {
  3492.                     if ( ((msg >= NativeMethods.WindowMessage.MouseFirst) && 
  3493.                         (msg <= NativeMethods.WindowMessage.MouseLast) )
  3494.                         || ((msg == NativeMethods.WindowMessage.MouseMove) && 
  3495.                         (State.IsNotifiedOnMouseMove) ) )
  3496.                     {
  3497.                         int xPos = NativeMethods.LoWord((uint)lParam.ToInt32());
  3498.                         int yPos = NativeMethods.HiWord((uint)lParam.ToInt32());
  3499.  
  3500.                         if (msg == NativeMethods.WindowMessage.MouseWheel)
  3501.                         {
  3502.                             // NativeMethods.WindowMessage.MouseWheel passes
  3503.                             // screen mouse coords, so convert them to client coords
  3504.                             System.Drawing.Point pt = new System.Drawing.Point(xPos, yPos);
  3505.                             NativeMethods.ScreenToClient(hWnd, ref pt);
  3506.                             xPos = pt.X; yPos = pt.Y;
  3507.                         }
  3508.  
  3509.                         int mouseWheelDelta = 0;
  3510.                         if (msg == NativeMethods.WindowMessage.MouseWheel)
  3511.                             mouseWheelDelta = NativeMethods.HiWord((uint)wParam.ToInt32());
  3512.  
  3513.                         short buttonState = NativeMethods.LoWord((uint)wParam.ToInt32());
  3514.                         bool leftButton = ((buttonState & (short)NativeMethods.MouseButtons.Left) != 0);
  3515.                         bool rightButton = ((buttonState & (short)NativeMethods.MouseButtons.Right) != 0);
  3516.                         bool middleButton = ((buttonState & (short)NativeMethods.MouseButtons.Middle) != 0);
  3517.                         bool sideButton1 = ((buttonState & (short)NativeMethods.MouseButtons.Side1) != 0);
  3518.                         bool sideButton2 = ((buttonState & (short)NativeMethods.MouseButtons.Side2) != 0);
  3519.  
  3520.                         // Call the end user callback now
  3521.                         State.MouseFunction(leftButton, rightButton, middleButton, sideButton1, sideButton2, mouseWheelDelta, xPos, yPos);
  3522.                     }
  3523.                 }
  3524.                 if (State.WndProcFunction != null)
  3525.                 {
  3526.                     bool doneProcessing = false;
  3527.                     // Pass all messages to the app's MsgProc callback, and don't 
  3528.                     // process further messages if the apps says not to.
  3529.                     IntPtr result = State.WndProcFunction(hWnd, msg, wParam, lParam, ref doneProcessing);
  3530.                     if (doneProcessing)
  3531.                         return result;
  3532.                 }
  3533.             }
  3534.  
  3535.             switch(msg)
  3536.             {
  3537.                 case NativeMethods.WindowMessage.Paint:
  3538.                     // Handle paint messages when the app is paused
  3539.                     if ( (State.Device != null) && (State.AreDeviceObjectsCreated) && 
  3540.                         (State.AreDeviceObjectsReset) && (!State.IsRenderingPaused) )
  3541.                     {
  3542.                         double time = State.CurrentTime;
  3543.                         float elapsedTime = State.ElapsedTime;
  3544.  
  3545.                         if (State.Settings != null && State.IsD3DSettingsDialogShowing)
  3546.                         {
  3547.                             // Clear the render target and the zbuffer 
  3548.                             State.Device.Clear(ClearFlags.Target, 0x00003F3F, 1.0f, 0);
  3549.  
  3550.                             // Render the scene
  3551.                             State.Device.BeginScene();
  3552.                             State.Settings.OnRender(elapsedTime);
  3553.                             State.Device.EndScene();
  3554.                         }
  3555.                         else
  3556.                         {
  3557.                             if (State.CallbackInterface != null)
  3558.                             {
  3559.                                 State.CallbackInterface.OnFrameRender(State.Device, time, elapsedTime);
  3560.                             }
  3561.                         }
  3562.  
  3563.                         // Now try to present
  3564.                         try
  3565.                         {
  3566.                             State.Device.Present();
  3567.                         }
  3568.                         catch(DeviceLostException)
  3569.                         {
  3570.                             // Device was lost
  3571.                             State.IsDeviceLost = true;
  3572.                         }
  3573.                         catch(DriverInternalErrorException)
  3574.                         {
  3575.                             // See Render3DEnvironment for information on this exception
  3576.                             State.IsDeviceLost = true;
  3577.                         }
  3578.                     }
  3579.                     break;
  3580.                 case NativeMethods.WindowMessage.Size:
  3581.                     // Pick up possible changes to window style due to maximize, etc.
  3582.                     if (IsWindowed && Window != IntPtr.Zero)
  3583.                         State.WindowStyle = NativeMethods.GetStyle(Window);
  3584.  
  3585.                     const int Minimized = 1;
  3586.                     const int Maximized = 2;
  3587.                     const int Restored = 0;
  3588.  
  3589.                     int sizeParam = wParam.ToInt32();
  3590.                     switch (sizeParam)
  3591.                     {
  3592.                         case Minimized:
  3593.                             if ((State.IsCursorClippedWhenFullScreen) && !(IsWindowed) )
  3594.                                 System.Windows.Forms.Cursor.Clip = System.Drawing.Rectangle.Empty;
  3595.                             Pause(true, true); // Pause while minimized
  3596.                             State.IsMinimized = true;
  3597.                             State.IsMaximized = false;
  3598.                             break;
  3599.                         case Maximized:
  3600.                             if (State.IsMinimized)
  3601.                                 Pause(false, false); // Unpause since we're no longer minimized
  3602.                             State.IsMinimized = false;
  3603.                             State.IsMaximized = true;
  3604.                             HandlePossibleSizeChange();
  3605.                             break;
  3606.                         case Restored:
  3607.                             if (State.IsMaximized)
  3608.                             {
  3609.                                 State.IsMaximized = false;
  3610.                                 HandlePossibleSizeChange();
  3611.                             }
  3612.                             else if (State.IsMinimized)
  3613.                             {
  3614.                                 Pause(false, false); // Unpause since we're no longer minimized
  3615.                                 State.IsMaximized = false;
  3616.                                 HandlePossibleSizeChange();
  3617.                             }
  3618.                             else
  3619.                             {
  3620.                                 // If we're neither maximized nor minimized, the window size 
  3621.                                 // is changing by the user dragging the wifndow edges.  In this 
  3622.                                 // case, we don't reset the device yet -- we wait until the 
  3623.                                 // user stops dragging, and a WindowMessage.ExitSizeMove message comes.
  3624.                             }
  3625.                             break;
  3626.                     }
  3627.                     break;
  3628.                 case NativeMethods.WindowMessage.GetMinMax:
  3629.                     // Get the min/max info for this window
  3630.                     NativeMethods.MinMaxInformation info = (NativeMethods.MinMaxInformation)System.Runtime.InteropServices.Marshal.PtrToStructure(lParam, typeof(NativeMethods.MinMaxInformation));
  3631.                     info.MinTrackSize = MinWindowSize;
  3632.                     // Copy this back
  3633.                     System.Runtime.InteropServices.Marshal.StructureToPtr(info, lParam, false);
  3634.  
  3635.                     break;
  3636.                 case NativeMethods.WindowMessage.EnterSizeMove:
  3637.                     // Halt frame movement while the app is sizing or moving
  3638.                     Pause(true, true);
  3639.                     break;
  3640.                 case NativeMethods.WindowMessage.ExitSizeMove:
  3641.                     // Go ahead
  3642.                     Pause(false, false);
  3643.                     HandlePossibleSizeChange();
  3644.                     break;
  3645.                 case NativeMethods.WindowMessage.SetCursor:
  3646.                     // Turn off Windows cursor in full screen mode
  3647.                     if ( (!State.IsRenderingPaused) && (!IsWindowed) )
  3648.                     {
  3649.                         System.Windows.Forms.Cursor.Current = null;
  3650.                         if ( (State.Device != null) && State.IsShowingCursorWhenFullScreen)
  3651.                             State.Device.ShowCursor(true);
  3652.  
  3653.                         return TrueIntPtr;
  3654.                     }
  3655.                     else
  3656.                         System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
  3657.                     break;
  3658.                 case NativeMethods.WindowMessage.MouseMove:
  3659.                     if ( (!State.IsRenderingPaused) && (!IsWindowed) )
  3660.                     {
  3661.                         if (State.Device != null)
  3662.                         {
  3663.                             State.Device.SetCursorPosition(System.Windows.Forms.Cursor.Position.X, 
  3664.                                 System.Windows.Forms.Cursor.Position.Y, false);
  3665.                         }
  3666.                     }
  3667.                     break;
  3668.                 case NativeMethods.WindowMessage.ActivateApplication:
  3669.                     if (wParam != IntPtr.Zero)
  3670.                     {
  3671.                         State.IsActive = true;
  3672.                     }
  3673.                     else
  3674.                     {
  3675.                         State.IsActive = false;
  3676.                     }
  3677.                     break;
  3678.                 case NativeMethods.WindowMessage.EnterMenuLoop:
  3679.                     // Halt frame movement while the menu is showing
  3680.                     Pause(true, true);
  3681.                     break;
  3682.                 case NativeMethods.WindowMessage.ExitMenuLoop:
  3683.                     // Go ahead
  3684.                     Pause(false, false);
  3685.                     break;
  3686.                 case NativeMethods.WindowMessage.NonClientHitTest:
  3687.                     // Prevent the user from selecting the menu in full screen mode
  3688.                     if (!IsWindowed)
  3689.                         return TrueIntPtr;
  3690.                     break;
  3691.                 case NativeMethods.WindowMessage.PowerBroadcast:
  3692.                     if (wParam == IntPtr.Zero) // Query Suspend
  3693.                     {
  3694.                         // At this point, the app should save any data for open
  3695.                         // network connections, files, etc., and prepare to go into
  3696.                         // a suspended mode.  The app can use the MsgProc callback
  3697.                         // to handle this if desired.
  3698.                         return TrueIntPtr;
  3699.                     }
  3700.                     if (wParam.ToInt32() == 7) // Resume Suspend
  3701.                     {
  3702.                         // At this point, the app should recover any data, network
  3703.                         // connections, files, etc., and resume running from when
  3704.                         // the app was suspended. The app can use the MsgProc callback
  3705.                         // to handle this if desired.
  3706.                         return TrueIntPtr;
  3707.                     }
  3708.                     break;
  3709.                 case NativeMethods.WindowMessage.SystemCommand:
  3710.                     // Prevent moving/sizing and power loss in full screen mode
  3711.                     const int SystemCommandSize = 0xF000;
  3712.                     const int SystemCommandMove = 0xF010;
  3713.                     const int SystemCommandMaximize = 0xF030;
  3714.                     const int SystemCommandKeyMenu = 0xF100;
  3715.                     const int SystemCommandMonitorPower = 0xF170;
  3716.                     switch(wParam.ToInt32())
  3717.                     {
  3718.                         case SystemCommandSize:
  3719.                         case SystemCommandMove:
  3720.                         case SystemCommandMaximize:
  3721.                         case SystemCommandKeyMenu:
  3722.                         case SystemCommandMonitorPower:
  3723.                             if (!IsWindowed)
  3724.                                 return TrueIntPtr;
  3725.                             break;
  3726.                     }
  3727.                     break;
  3728.                 case NativeMethods.WindowMessage.SystemCharacter:
  3729.                     // Toggle fullscreen
  3730.                     if (State.IsHandlingDefaultHotkeys)
  3731.                     {
  3732.                         // They hit return
  3733.                         if (wParam.ToInt32() == (int)System.Windows.Forms.Keys.Return)
  3734.                         {
  3735.                             // Was 'alt' held down?
  3736.                             uint mask = (1 << 29);
  3737.                             if ((lParam.ToInt32() & mask) != 0)
  3738.                             {
  3739.                                 // Toggle the full screen/window mode
  3740.                                 Pause(true, true);
  3741.                                 ToggleFullscreen();
  3742.                                 Pause(false, false);                        
  3743.                                 return IntPtr.Zero;
  3744.                             }
  3745.                         }
  3746.                     }
  3747.                     break;
  3748.                 case NativeMethods.WindowMessage.KeyDown:
  3749.                     if (State.IsHandlingDefaultHotkeys)
  3750.                     {
  3751.                         // Handle the default keys
  3752.                         System.Windows.Forms.Keys key = (System.Windows.Forms.Keys)wParam.ToInt32();
  3753.                         switch(key)
  3754.                         {
  3755.                             case System.Windows.Forms.Keys.F2:
  3756.                                 ShowSettingsDialog(!State.IsD3DSettingsDialogShowing);
  3757.                                 break;
  3758.                             case System.Windows.Forms.Keys.F3: // Toggle reference
  3759.                                 Pause(true, true);
  3760.                                 ToggleReference();
  3761.                                 Pause(false, false);
  3762.                                 break;
  3763.  
  3764.                             case System.Windows.Forms.Keys.F8:
  3765.                                 State.IsInWireframeMode = !State.IsInWireframeMode;
  3766.                                 if (State.Device != null)
  3767.                                 {
  3768.                                     State.Device.RenderState.FillMode = State.IsInWireframeMode ? FillMode.WireFrame : FillMode.Solid;
  3769.                                 }
  3770.                                 break;
  3771.  
  3772.                             case System.Windows.Forms.Keys.Escape:
  3773.                                 // Received key to exit app
  3774.                                 NativeMethods.SendMessage(hWnd, NativeMethods.WindowMessage.Close, IntPtr.Zero, IntPtr.Zero);
  3775.                                 break;
  3776.  
  3777.                             case System.Windows.Forms.Keys.Pause:
  3778.                                 bool isTimePaused = (State.PauseTimeCount > 0);
  3779.                                 isTimePaused = !isTimePaused;
  3780.                                 if (isTimePaused)
  3781.                                     Pause(true, false);
  3782.                                 else
  3783.                                     Pause(false, false);
  3784.  
  3785.                                 break;
  3786.  
  3787.                         }
  3788.                     }
  3789.                     break;
  3790.                 case NativeMethods.WindowMessage.Close:
  3791.                     NativeMethods.DestroyWindow(hWnd);
  3792.                     NativeMethods.UnregisterClass(WindowClassName, IntPtr.Zero);
  3793.                     // No more windows
  3794.                     State.WindowFocus = IntPtr.Zero;
  3795.                     State.WindowDeviceWindowed = IntPtr.Zero;
  3796.                     State.WindowDeviceFullScreen = IntPtr.Zero;
  3797.                     return IntPtr.Zero; 
  3798.                 case NativeMethods.WindowMessage.Destroy:
  3799.                     NativeMethods.PostQuitMessage(0);
  3800.                     break;
  3801.             }
  3802.             return NativeMethods.DefWindowProc(hWnd, msg, wParam, lParam);
  3803.         }
  3804.         #endregion
  3805.  
  3806.         /// <summary>
  3807.         /// Handles app's message loop and rendering when idle.  If CreateDevice*() or SetDevice() 
  3808.         /// has not already been called, it will call CreateDevice() with the default parameters.  
  3809.         /// </summary>
  3810.         public void MainLoop()
  3811.         {
  3812.             // Not allowed to call this from inside the device callbacks or reenter
  3813.             if (State.IsInsideDeviceCallback || State.IsInsideMainloop)
  3814.             {
  3815.                 State.ApplicationExitCode = 1;
  3816.                 throw new InvalidOperationException("You cannot call this method from a callback, or reenter the method.");
  3817.             }
  3818.  
  3819.             // We're inside the loop now
  3820.             State.IsInsideMainloop = true;
  3821.  
  3822.             // If CreateDevice*() or SetDevice() has not already been called, 
  3823.             // then call CreateDevice() with the default parameters.         
  3824.             if (!State.WasDeviceCreated)
  3825.             {
  3826.                 if (State.WasDeviceCreateCalled)
  3827.                 {
  3828.                     // It already called and failed, don't try again
  3829.                     State.ApplicationExitCode = 1;
  3830.                     throw new InvalidOperationException("The device was never created.");
  3831.                 }
  3832.  
  3833.                 try
  3834.                 {
  3835.                     CreateDevice(0, true, 640, 480, null);
  3836.                 }
  3837.                 catch
  3838.                 {
  3839.                     // Well, can't do anything, just set the exit code and rethrow
  3840.                     State.ApplicationExitCode = 1;
  3841.                     throw;
  3842.                 }
  3843.             }
  3844.  
  3845.             // Initialize() must have been called and succeeded for this function to proceed
  3846.             // CreateWindow() or SetWindow() must have been called and succeeded for this function to proceed
  3847.             // CreateDevice() or CreateDeviceFromSettings() or SetDevice() must have been called and succeeded for this function to proceed
  3848.             if ( (!State.IsInited) || (!State.WasWindowCreated) || (!State.WasDeviceCreated) )
  3849.             {
  3850.                 State.ApplicationExitCode = 1;
  3851.                 throw new InvalidOperationException("The framework was not initialized, cannot continue.");
  3852.             }
  3853.  
  3854.             bool gotMessage = false;
  3855.             NativeMethods.Message msg;
  3856.  
  3857.             // Get the first message
  3858.             NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, NativeMethods.PeekMessageFlags.NoRemove);
  3859.  
  3860.             while(msg.msg != NativeMethods.WindowMessage.Quit)
  3861.             {
  3862.                 gotMessage = NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, NativeMethods.PeekMessageFlags.Remove);
  3863.                 if (gotMessage)
  3864.                 {
  3865.                     NativeMethods.TranslateMessage(ref msg);
  3866.                     NativeMethods.DispatchMessage(ref msg);
  3867.                 }
  3868.                 else
  3869.                 {
  3870.                     // Render a frame during idle time (no messages are waiting)
  3871.                     Render3DEnvironment();
  3872.                 }
  3873.             }
  3874.  
  3875.             State.IsInsideMainloop = false;
  3876.         }
  3877.  
  3878.         #region IDisposable Members
  3879.         /// <summary>Shut down the sample framework</summary>
  3880.         public void Dispose()
  3881.         {
  3882.             // Disable finalization of this object now
  3883.             GC.SuppressFinalize(this);
  3884.  
  3885.             if (!isDisposed)
  3886.             {
  3887.                 // Raise the event if need be
  3888.                 if (Disposing != null)
  3889.                     Disposing(this, EventArgs.Empty);
  3890.  
  3891.                 // Now shut down the system
  3892.                 Shutdown();
  3893.             }
  3894.             
  3895.             // The object has now been disposed
  3896.             isDisposed = true;
  3897.         }
  3898.  
  3899.         /// <summary>In case they forget to dispose</summary>
  3900.         ~Framework()
  3901.         {
  3902.             Dispose();
  3903.         }
  3904.         #endregion
  3905.  
  3906.         #region Device event handlers
  3907.         /// <summary>Fired when the device is lost</summary>
  3908.         private void OnDeviceLost(object sender, EventArgs e)
  3909.         {
  3910.             // Mark as being inside a callback
  3911.             State.IsInsideDeviceCallback = true;
  3912.  
  3913.             // Release all vidmem objects
  3914.             if (State.AreDeviceObjectsReset)
  3915.             {
  3916.                 DialogResourceManager.GetGlobalInstance().OnLostDevice();
  3917.                 ResourceCache.GetGlobalInstance().OnLostDevice();
  3918.                 State.AreDeviceObjectsReset = false;
  3919.             }
  3920.  
  3921.             // Pass this event along
  3922.             if (DeviceLost != null)
  3923.                 DeviceLost(this, e);
  3924.  
  3925.             // Now you're not
  3926.             State.IsInsideDeviceCallback = false;
  3927.         }
  3928.  
  3929.         /// <summary>Fired when the device is reset</summary>
  3930.         private void OnDeviceRest(object sender, EventArgs e)
  3931.         {
  3932.             // Get the device
  3933.             Device device = sender as Device;
  3934.             System.Diagnostics.Debug.Assert(device != null, "Must have a device here.");
  3935.  
  3936.             // Mark as being inside a callback
  3937.             State.IsInsideDeviceCallback = true;
  3938.  
  3939.             // Prepare the device with cursor info and 
  3940.             // store backbuffer desc and caps from the device
  3941.             PrepareDevice(device);
  3942.  
  3943.             if (State.Settings != null)
  3944.                 State.Settings.OnResetDevice();
  3945.  
  3946.             // Call the dialog resource manager device reset function
  3947.             DialogResourceManager.GetGlobalInstance().OnResetDevice(device);
  3948.             // Call the resource cache device reset function
  3949.             ResourceCache.GetGlobalInstance().OnResetDevice(device);
  3950.  
  3951.             // Call app's reset callback
  3952.             if (DeviceReset != null)
  3953.                 DeviceReset(this, new DeviceEventArgs(device, State.BackBufferSurfaceDesc));
  3954.  
  3955.             // Device objects are reset now
  3956.             State.AreDeviceObjectsReset = true;
  3957.             // Now you're not
  3958.             State.IsInsideDeviceCallback = false;
  3959.         }
  3960.         #endregion
  3961.     }
  3962. }